blob: 24ee91b3bd9d29d6bbf70d215568858c839e1e5c [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
Tao Baod6085d62019-05-06 12:55:42 -070094 --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
95 vbmeta_vendor}_algorithm <algorithm>
96 --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
97 vbmeta_vendor}_key <key>
Tao Bao639118f2017-06-19 15:48:02 -070098 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
99 the specified image. Otherwise it uses the existing values in info dict.
100
Tao Baod6085d62019-05-06 12:55:42 -0700101 --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system,
102 vbmeta_vendor}_extra_args <args>
Tao Bao639118f2017-06-19 15:48:02 -0700103 Specify any additional args that are needed to AVB-sign the image
104 (e.g. "--signing_helper /path/to/helper"). The args will be appended to
105 the existing ones in info dict.
Doug Zongkereef39442009-04-02 12:14:19 -0700106"""
107
Tao Bao0c28d2d2017-12-24 10:37:38 -0800108from __future__ import print_function
Doug Zongkereef39442009-04-02 12:14:19 -0700109
Robert Craig817c5742013-04-19 10:59:22 -0400110import base64
Doug Zongker8e931bf2009-04-06 15:21:45 -0700111import copy
Robert Craig817c5742013-04-19 10:59:22 -0400112import errno
Narayan Kamatha07bf042017-08-14 14:49:21 +0100113import gzip
Tao Baoaa7e9932019-03-15 09:37:01 -0700114import itertools
Tao Baobadceb22019-03-15 09:33:43 -0700115import logging
Doug Zongkereef39442009-04-02 12:14:19 -0700116import os
117import re
Narayan Kamatha07bf042017-08-14 14:49:21 +0100118import shutil
Tao Bao9fdd00f2017-07-12 11:57:05 -0700119import stat
Doug Zongkereef39442009-04-02 12:14:19 -0700120import subprocess
Tao Bao0c28d2d2017-12-24 10:37:38 -0800121import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700122import tempfile
123import zipfile
Tao Bao66472632017-12-04 17:16:36 -0800124from xml.etree import ElementTree
Doug Zongkereef39442009-04-02 12:14:19 -0700125
Doug Zongker3c84f562014-07-31 11:06:30 -0700126import add_img_to_target_files
Tao Baoaa7e9932019-03-15 09:37:01 -0700127import apex_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700128import common
129
Tao Bao0c28d2d2017-12-24 10:37:38 -0800130
131if sys.hexversion < 0x02070000:
132 print("Python 2.7 or newer is required.", file=sys.stderr)
133 sys.exit(1)
134
135
Tao Baobadceb22019-03-15 09:33:43 -0700136logger = logging.getLogger(__name__)
137
Doug Zongkereef39442009-04-02 12:14:19 -0700138OPTIONS = common.OPTIONS
139
140OPTIONS.extra_apks = {}
Tao Baoaa7e9932019-03-15 09:37:01 -0700141OPTIONS.extra_apex_payload_keys = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700142OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700143OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700144OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700145OPTIONS.replace_ota_keys = False
Geremy Condraf19b3652014-07-29 17:54:54 -0700146OPTIONS.replace_verity_public_key = False
147OPTIONS.replace_verity_private_key = False
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700148OPTIONS.replace_verity_keyid = False
Doug Zongker831840e2011-09-22 10:28:04 -0700149OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700150OPTIONS.avb_keys = {}
151OPTIONS.avb_algorithms = {}
152OPTIONS.avb_extra_args = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700153
Tao Bao0c28d2d2017-12-24 10:37:38 -0800154
Narayan Kamatha07bf042017-08-14 14:49:21 +0100155def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800156 # apply the key remapping to the contents of the file
157 for apk, cert in certmap.iteritems():
158 certmap[apk] = OPTIONS.key_map.get(cert, cert)
159
160 # apply all the -e options, overriding anything in the file
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700161 for apk, cert in OPTIONS.extra_apks.iteritems():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800162 if not cert:
163 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700164 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800165
Doug Zongkereef39442009-04-02 12:14:19 -0700166 return certmap
167
168
Tao Baoaa7e9932019-03-15 09:37:01 -0700169def GetApexKeys(keys_info, key_map):
170 """Gets APEX payload and container signing keys by applying the mapping rules.
171
Tao Baoe1343992019-03-19 12:24:03 -0700172 Presigned payload / container keys will be set accordingly.
Tao Baoaa7e9932019-03-15 09:37:01 -0700173
174 Args:
175 keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
176 container_key).
177 key_map: A dict that overrides the keys, specified via command-line input.
178
179 Returns:
180 A dict that contains the updated APEX key mapping, which should be used for
181 the current signing.
Tao Baof98fa102019-04-24 14:51:25 -0700182
183 Raises:
184 AssertionError: On invalid container / payload key overrides.
Tao Baoaa7e9932019-03-15 09:37:01 -0700185 """
186 # Apply all the --extra_apex_payload_key options to override the payload
187 # signing keys in the given keys_info.
188 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700189 if not key:
190 key = 'PRESIGNED'
Tao Baoaa7e9932019-03-15 09:37:01 -0700191 keys_info[apex] = (key, keys_info[apex][1])
192
193 # Apply the key remapping to container keys.
194 for apex, (payload_key, container_key) in keys_info.items():
195 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
196
197 # Apply all the --extra_apks options to override the container keys.
198 for apex, key in OPTIONS.extra_apks.items():
199 # Skip non-APEX containers.
200 if apex not in keys_info:
201 continue
Tao Baoe1343992019-03-19 12:24:03 -0700202 if not key:
203 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700204 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700205
Tao Baof98fa102019-04-24 14:51:25 -0700206 # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
207 # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
208 # (overridden via commandline) indicates a config error, which should not be
209 # allowed.
210 for apex, (payload_key, container_key) in keys_info.items():
211 if container_key != 'PRESIGNED':
212 continue
213 if apex in OPTIONS.extra_apex_payload_keys:
214 payload_override = OPTIONS.extra_apex_payload_keys[apex]
215 assert payload_override == '', \
216 ("Invalid APEX key overrides: {} has PRESIGNED container but "
217 "non-PRESIGNED payload key {}").format(apex, payload_override)
218 if payload_key != 'PRESIGNED':
219 print(
220 "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
221 apex))
222 keys_info[apex] = ('PRESIGNED', 'PRESIGNED')
223
Tao Baoaa7e9932019-03-15 09:37:01 -0700224 return keys_info
225
226
Tao Bao93c2a012018-06-19 12:19:35 -0700227def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700228 """Returns the APK info based on the given filename.
229
230 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700231 compressed extension into consideration. If it appears to be an APK file,
232 further checks if the APK file should be skipped when signing, based on the
233 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700234
235 Args:
236 filename: Path to the file.
237 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
238 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700239 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700240
241 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700242 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
243 given filename is an APK file. is_compressed indicates whether the APK file
244 is compressed (only meaningful when is_apk is True). should_be_skipped
245 indicates whether the filename matches any of the given prefixes to be
246 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700247
248 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700249 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700250 """
251 assert compressed_extension is None or compressed_extension.startswith('.'), \
252 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
253
Tao Bao93c2a012018-06-19 12:19:35 -0700254 # skipped_prefixes should be one of set/list/tuple types. Other types such as
255 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700256 assert isinstance(skipped_prefixes, (set, list, tuple)), \
257 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700258
Tao Bao11f955c2018-06-19 12:19:35 -0700259 compressed_apk_extension = (
260 ".apk" + compressed_extension if compressed_extension else None)
261 is_apk = (filename.endswith(".apk") or
262 (compressed_apk_extension and
263 filename.endswith(compressed_apk_extension)))
264 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700265 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700266
267 is_compressed = (compressed_apk_extension and
268 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700269 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
270 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700271
272
Tao Baoaa7e9932019-03-15 09:37:01 -0700273def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700274 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700275 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700276
277 Args:
278 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700279 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700280 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700281 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700282 apex_keys: A dict that contains the key mapping from APEX name to
283 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700284
285 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700286 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700287 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700288 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700289 for info in input_tf_zip.infolist():
Tao Baoaa7e9932019-03-15 09:37:01 -0700290 # Handle APEXes first, e.g. SYSTEM/apex/com.android.tzdata.apex.
291 if (info.filename.startswith('SYSTEM/apex') and
292 info.filename.endswith('.apex')):
293 name = os.path.basename(info.filename)
294 if name not in known_keys:
295 unknown_files.append(name)
296 continue
297
298 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700299 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
300 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
301 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700302 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700303
Tao Bao11f955c2018-06-19 12:19:35 -0700304 name = os.path.basename(info.filename)
305 if is_compressed:
306 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700307 if name not in known_keys:
308 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700309
Tao Baoaa7e9932019-03-15 09:37:01 -0700310 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700311 ("No key specified for:\n {}\n"
312 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700313 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700314
Tao Baoe1343992019-03-19 12:24:03 -0700315 # For all the APEXes, double check that we won't have an APEX that has only
Tao Baof98fa102019-04-24 14:51:25 -0700316 # one of the payload / container keys set. Note that non-PRESIGNED container
317 # with PRESIGNED payload could be allowed but currently unsupported. It would
318 # require changing SignApex implementation.
Tao Baoe1343992019-03-19 12:24:03 -0700319 if not apex_keys:
320 return
321
322 invalid_apexes = []
323 for info in input_tf_zip.infolist():
324 if (not info.filename.startswith('SYSTEM/apex') or
325 not info.filename.endswith('.apex')):
326 continue
327
328 name = os.path.basename(info.filename)
329 (payload_key, container_key) = apex_keys[name]
330 if ((payload_key in common.SPECIAL_CERT_STRINGS and
331 container_key not in common.SPECIAL_CERT_STRINGS) or
332 (payload_key not in common.SPECIAL_CERT_STRINGS and
333 container_key in common.SPECIAL_CERT_STRINGS)):
334 invalid_apexes.append(
335 "{}: payload_key {}, container_key {}".format(
336 name, payload_key, container_key))
337
338 assert not invalid_apexes, \
339 "Invalid APEX keys specified:\n {}\n".format(
340 "\n ".join(invalid_apexes))
341
Doug Zongkereb338ef2009-05-20 16:50:49 -0700342
Narayan Kamatha07bf042017-08-14 14:49:21 +0100343def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
344 is_compressed):
Doug Zongkereef39442009-04-02 12:14:19 -0700345 unsigned = tempfile.NamedTemporaryFile()
346 unsigned.write(data)
347 unsigned.flush()
348
Narayan Kamatha07bf042017-08-14 14:49:21 +0100349 if is_compressed:
350 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800351 with gzip.open(unsigned.name, "rb") as in_file, \
352 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100353 shutil.copyfileobj(in_file, out_file)
354
355 # Finally, close the "unsigned" file (which is gzip compressed), and then
356 # replace it with the uncompressed version.
357 #
358 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
359 # we could just gzip / gunzip in-memory buffers instead.
360 unsigned.close()
361 unsigned = uncompressed
362
Doug Zongkereef39442009-04-02 12:14:19 -0700363 signed = tempfile.NamedTemporaryFile()
364
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800365 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
366 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
367 # didn't change, we don't want its signature to change due to the switch
368 # from SHA-1 to SHA-256.
369 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
370 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
371 # that the APK's minSdkVersion is 1.
372 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
373 # determine whether to use SHA-256.
374 min_api_level = None
375 if platform_api_level > 23:
376 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
377 # minSdkVersion attribute
378 min_api_level = None
379 else:
380 # Force APK signer to use SHA-1
381 min_api_level = 1
382
383 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800384 min_api_level=min_api_level,
385 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700386
Tao Bao0c28d2d2017-12-24 10:37:38 -0800387 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100388 if is_compressed:
389 # Recompress the file after it has been signed.
390 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800391 with open(signed.name, "rb") as in_file, \
392 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100393 shutil.copyfileobj(in_file, out_file)
394
395 data = compressed.read()
396 compressed.close()
397 else:
398 data = signed.read()
399
Doug Zongkereef39442009-04-02 12:14:19 -0700400 unsigned.close()
401 signed.close()
402
403 return data
404
405
Tao Baoaa7e9932019-03-15 09:37:01 -0700406def SignApex(apex_data, payload_key, container_key, container_pw,
407 codename_to_api_level_map, signing_args=None):
408 """Signs the current APEX with the given payload/container keys.
409
410 Args:
411 apex_data: Raw APEX data.
Tao Bao9c0f4432019-04-01 21:25:05 -0700412 payload_key: The path to payload signing key (w/ extension).
Tao Baoaa7e9932019-03-15 09:37:01 -0700413 container_key: The path to container signing key (w/o extension).
414 container_pw: The matching password of the container_key, or None.
415 codename_to_api_level_map: A dict that maps from codename to API level.
416 signing_args: Additional args to be passed to the payload signer.
417
418 Returns:
Tao Bao9c0f4432019-04-01 21:25:05 -0700419 The path to the signed APEX file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700420 """
421 apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
422 with open(apex_file, 'wb') as apex_fp:
423 apex_fp.write(apex_data)
424
425 APEX_PAYLOAD_IMAGE = 'apex_payload.img'
Tao Bao9c0f4432019-04-01 21:25:05 -0700426 APEX_PUBKEY = 'apex_pubkey'
Tao Baoaa7e9932019-03-15 09:37:01 -0700427
Tao Bao9c0f4432019-04-01 21:25:05 -0700428 # 1a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
429 # payload_key.
Tao Baoaa7e9932019-03-15 09:37:01 -0700430 payload_dir = common.MakeTempDir(prefix='apex-payload-')
431 with zipfile.ZipFile(apex_file) as apex_fd:
432 payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
433
434 payload_info = apex_utils.ParseApexPayloadInfo(payload_file)
435 apex_utils.SignApexPayload(
436 payload_file,
437 payload_key,
438 payload_info['apex.key'],
439 payload_info['Algorithm'],
440 payload_info['Salt'],
441 signing_args)
442
Tao Bao9c0f4432019-04-01 21:25:05 -0700443 # 1b. Update the embedded payload public key.
444 payload_public_key = common.ExtractAvbPublicKey(payload_key)
445
Tao Baoaa7e9932019-03-15 09:37:01 -0700446 common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
Tao Bao9c0f4432019-04-01 21:25:05 -0700447 common.ZipDelete(apex_file, APEX_PUBKEY)
Tao Baoaa7e9932019-03-15 09:37:01 -0700448 apex_zip = zipfile.ZipFile(apex_file, 'a')
449 common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
Tao Bao9c0f4432019-04-01 21:25:05 -0700450 common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
Tao Baoaa7e9932019-03-15 09:37:01 -0700451 common.ZipClose(apex_zip)
452
Tao Baoffc9a302019-03-22 23:16:58 -0700453 # 2. Align the files at page boundary (same as in apexer).
454 aligned_apex = common.MakeTempFile(
455 prefix='apex-container-', suffix='.apex')
456 common.RunAndCheckOutput(
457 ['zipalign', '-f', '4096', apex_file, aligned_apex])
458
459 # 3. Sign the APEX container with container_key.
Tao Baoaa7e9932019-03-15 09:37:01 -0700460 signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
Tao Baoffc9a302019-03-22 23:16:58 -0700461
462 # Specify the 4K alignment when calling SignApk.
463 extra_signapk_args = OPTIONS.extra_signapk_args[:]
464 extra_signapk_args.extend(['-a', '4096'])
465
Tao Baoaa7e9932019-03-15 09:37:01 -0700466 common.SignFile(
Tao Baoffc9a302019-03-22 23:16:58 -0700467 aligned_apex,
Tao Baoaa7e9932019-03-15 09:37:01 -0700468 signed_apex,
469 container_key,
470 container_pw,
Tao Baoffc9a302019-03-22 23:16:58 -0700471 codename_to_api_level_map=codename_to_api_level_map,
472 extra_signapk_args=extra_signapk_args)
Tao Baoaa7e9932019-03-15 09:37:01 -0700473
Tao Bao9c0f4432019-04-01 21:25:05 -0700474 return signed_apex
Tao Baoaa7e9932019-03-15 09:37:01 -0700475
476
Doug Zongker412c02f2014-02-13 10:58:24 -0800477def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700478 apk_keys, apex_keys, key_passwords,
479 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100480 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700481 # maxsize measures the maximum filename length, including the ones to be
482 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800483 maxsize = max(
484 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700485 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700486 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800487
Doug Zongkereef39442009-04-02 12:14:19 -0700488 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700489 filename = info.filename
490 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700491 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700492
Tao Bao33bf2682019-01-11 12:37:35 -0800493 # Skip split super images, which will be re-generated during signing.
494 if filename.startswith("OTA/") and filename.endswith(".img"):
495 continue
496
Tao Bao11f955c2018-06-19 12:19:35 -0700497 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700498 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700499 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
500 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
501
502 if is_apk and should_be_skipped:
503 # Copy skipped APKs verbatim.
504 print(
505 "NOT signing: %s\n"
506 " (skipped due to matching prefix)" % (filename,))
507 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800508
Tao Baof2cffbd2015-07-22 12:33:18 -0700509 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700510 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700511 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100512 if is_compressed:
513 name = name[:-len(compressed_extension)]
514
Tao Baoaa7e9932019-03-15 09:37:01 -0700515 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800516 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800517 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800518 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800519 codename_to_api_level_map, is_compressed)
Tao Bao2ed665a2015-04-01 11:21:55 -0700520 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700521 else:
522 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700523 print(
524 "NOT signing: %s\n"
525 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700526 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700527
Tao Baoaa7e9932019-03-15 09:37:01 -0700528 # Sign bundled APEX files.
529 elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
530 name = os.path.basename(filename)
531 payload_key, container_key = apex_keys[name]
532
Tao Baoe1343992019-03-19 12:24:03 -0700533 # We've asserted not having a case with only one of them PRESIGNED.
534 if (payload_key not in common.SPECIAL_CERT_STRINGS and
535 container_key not in common.SPECIAL_CERT_STRINGS):
536 print(" signing: %-*s container (%s)" % (
537 maxsize, name, container_key))
538 print(" : %-*s payload (%s)" % (
539 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700540
Tao Bao9c0f4432019-04-01 21:25:05 -0700541 signed_apex = SignApex(
Tao Baoe1343992019-03-19 12:24:03 -0700542 data,
543 payload_key,
544 container_key,
545 key_passwords[container_key],
546 codename_to_api_level_map,
547 OPTIONS.avb_extra_args.get('apex'))
548 common.ZipWrite(output_tf_zip, signed_apex, filename)
Tao Baoaa7e9932019-03-15 09:37:01 -0700549
Tao Baoe1343992019-03-19 12:24:03 -0700550 else:
551 print(
552 "NOT signing: %s\n"
553 " (skipped due to special cert string)" % (name,))
554 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700555
556 # AVB public keys for the installed APEXes, which will be updated later.
557 elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
558 filename != 'SYSTEM/etc/security/apex/'):
559 continue
560
Tao Baoa80ed222016-06-16 14:41:24 -0700561 # System properties.
Tao Bao11f955c2018-06-19 12:19:35 -0700562 elif filename in ("SYSTEM/build.prop",
563 "VENDOR/build.prop",
Magnus Strandh234f4b42019-05-01 23:09:30 +0200564 "SYSTEM/vendor/build.prop",
565 "ODM/build.prop",
566 "VENDOR/odm/build.prop",
567 "PRODUCT/build.prop",
568 "SYSTEM/product/build.prop",
569 "PRODUCT_SERVICES/build.prop",
570 "SYSTEM/product_services/build.prop",
Tao Bao11f955c2018-06-19 12:19:35 -0700571 "SYSTEM/etc/prop.default",
572 "BOOT/RAMDISK/prop.default",
573 "BOOT/RAMDISK/default.prop", # legacy
574 "ROOT/default.prop", # legacy
575 "RECOVERY/RAMDISK/prop.default",
576 "RECOVERY/RAMDISK/default.prop"): # legacy
577 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800578 if stat.S_ISLNK(info.external_attr >> 16):
579 new_data = data
580 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800581 new_data = RewriteProps(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700582 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700583
Tao Bao66472632017-12-04 17:16:36 -0800584 # Replace the certs in *mac_permissions.xml (there could be multiple, such
585 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700586 elif filename.endswith("mac_permissions.xml"):
587 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400588 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700589 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700590
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700591 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700592 elif filename in ("SYSTEM/recovery-from-boot.p",
593 "SYSTEM/etc/recovery.img",
594 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700595 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700596
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700597 # Don't copy OTA certs if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700598 elif (
599 OPTIONS.replace_ota_keys and
600 filename in (
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700601 "BOOT/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700602 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700603 "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700604 "SYSTEM/etc/security/otacerts.zip",
605 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800606 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700607
Tao Bao46a59992017-06-05 11:55:16 -0700608 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700609 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700610 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700611
612 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700613 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700614 filename in ("BOOT/RAMDISK/verity_key",
615 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700616 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700617
Tao Bao8adcfd12016-06-17 17:01:22 -0700618 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700619 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700620 pass
621
Tianjie Xu4f099002016-08-11 18:04:27 -0700622 # Skip the care_map as we will regenerate the system/vendor images.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700623 elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700624 pass
625
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800626 # Updates system_other.avbpubkey in /product/etc/.
627 elif filename in (
628 "PRODUCT/etc/security/avb/system_other.avbpubkey",
629 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
630 # Only update system_other's public key, if the corresponding signing
631 # key is specified via --avb_system_other_key.
632 signing_key = OPTIONS.avb_keys.get("system_other")
633 if signing_key:
634 public_key = common.ExtractAvbPublicKey(signing_key)
635 print(" Rewriting AVB public key of system_other in /product")
636 common.ZipWrite(output_tf_zip, public_key, filename)
637
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800638 # Should NOT sign boot-debug.img.
639 elif filename in (
640 "BOOT/RAMDISK/force_debuggable",
641 "RECOVERY/RAMDISK/force_debuggable"
642 "RECOVERY/RAMDISK/first_stage_ramdisk/force_debuggable"):
643 raise common.ExternalError("debuggable boot.img cannot be signed")
644
Tao Baoa80ed222016-06-16 14:41:24 -0700645 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700646 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700647 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700648
Doug Zongker412c02f2014-02-13 10:58:24 -0800649 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700650 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800651
Tao Bao46a59992017-06-05 11:55:16 -0700652 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700653 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700654 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700655
656 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800657 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700658 # We are replacing the one in boot image only, since the one under
659 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700660 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700661 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700662
663 # Replace the keyid string in BOOT/cmdline.
664 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700665 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
666 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800667
Tao Bao639118f2017-06-19 15:48:02 -0700668 # Replace the AVB signing keys, if any.
669 ReplaceAvbSigningKeys(misc_info)
670
Tao Bao46a59992017-06-05 11:55:16 -0700671 # Write back misc_info with the latest values.
672 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
673
Doug Zongker8e931bf2009-04-06 15:21:45 -0700674
Robert Craig817c5742013-04-19 10:59:22 -0400675def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800676 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400677
Tao Bao66472632017-12-04 17:16:36 -0800678 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
679 be skipped. After the replacement, it additionally checks for duplicate
680 entries, which would otherwise fail the policy loading code in
681 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
682
683 Args:
684 data: Input string that contains a set of X.509 certs.
685
686 Returns:
687 A string after the replacement.
688
689 Raises:
690 AssertionError: On finding duplicate entries.
691 """
692 for old, new in OPTIONS.key_map.iteritems():
693 if OPTIONS.verbose:
694 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
695
696 try:
697 with open(old + ".x509.pem") as old_fp:
698 old_cert16 = base64.b16encode(
699 common.ParseCertificate(old_fp.read())).lower()
700 with open(new + ".x509.pem") as new_fp:
701 new_cert16 = base64.b16encode(
702 common.ParseCertificate(new_fp.read())).lower()
703 except IOError as e:
704 if OPTIONS.verbose or e.errno != errno.ENOENT:
705 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
706 "%s.x509.pem." % (e.filename, e.strerror, old, new))
707 continue
708
709 # Only match entire certs.
710 pattern = "\\b" + old_cert16 + "\\b"
711 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
712
713 if OPTIONS.verbose:
714 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
715 num, old, new))
716
717 # Verify that there're no duplicate entries after the replacement. Note that
718 # it's only checking entries with global seinfo at the moment (i.e. ignoring
719 # the ones with inner packages). (Bug: 69479366)
720 root = ElementTree.fromstring(data)
721 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
722 assert len(signatures) == len(set(signatures)), \
723 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400724
725 return data
726
727
Doug Zongkerc09abc82010-01-11 13:09:15 -0800728def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800729 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
730
731 Args:
732 tags: The input string that contains comma-separated tags.
733
734 Returns:
735 The updated tags (comma-separated and sorted).
736 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800737 tags = set(tags.split(","))
738 for ch in OPTIONS.tag_changes:
739 if ch[0] == "-":
740 tags.discard(ch[1:])
741 elif ch[0] == "+":
742 tags.add(ch[1:])
743 return ",".join(sorted(tags))
744
745
Tao Baoa7054ee2017-12-08 14:42:16 -0800746def RewriteProps(data):
747 """Rewrites the system properties in the given string.
748
749 Each property is expected in 'key=value' format. The properties that contain
750 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
751 EditTags().
752
753 Args:
754 data: Input string, separated by newlines.
755
756 Returns:
757 The string with modified properties.
758 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700759 output = []
760 for line in data.split("\n"):
761 line = line.strip()
762 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700763 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700764 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200765 if (key.startswith("ro.") and
766 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800767 pieces = value.split("/")
768 pieces[-1] = EditTags(pieces[-1])
769 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700770 elif key == "ro.bootimage.build.fingerprint":
771 pieces = value.split("/")
772 pieces[-1] = EditTags(pieces[-1])
773 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700774 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800775 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700776 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800777 pieces[-1] = EditTags(pieces[-1])
778 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200779 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800780 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700781 elif key == "ro.build.display.id":
782 # change, eg, "JWR66N dev-keys" to "JWR66N"
783 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700784 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800785 value.pop()
786 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800787 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700788 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800789 print(" replace: ", original_line)
790 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700791 output.append(line)
792 return "\n".join(output) + "\n"
793
794
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700795def WriteOtacerts(output_zip, filename, keys):
796 """Constructs a zipfile from given keys; and writes it to output_zip.
797
798 Args:
799 output_zip: The output target_files zip.
800 filename: The archive name in the output zip.
801 keys: A list of public keys to use during OTA package verification.
802 """
803
804 try:
805 from StringIO import StringIO
806 except ImportError:
807 from io import StringIO
808 temp_file = StringIO()
809 certs_zip = zipfile.ZipFile(temp_file, "w")
810 for k in keys:
811 common.ZipWrite(certs_zip, k)
812 common.ZipClose(certs_zip)
813 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
814
815
Doug Zongker831840e2011-09-22 10:28:04 -0700816def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700817 try:
818 keylist = input_tf_zip.read("META/otakeys.txt").split()
819 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700820 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700821
Tao Baof718f902017-11-09 10:10:10 -0800822 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800823 if extra_recovery_keys:
824 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
825 for k in extra_recovery_keys.split()]
826 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800827 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800828 else:
829 extra_recovery_keys = []
830
Doug Zongker8e931bf2009-04-06 15:21:45 -0700831 mapped_keys = []
832 for k in keylist:
833 m = re.match(r"^(.*)\.x509\.pem$", k)
834 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800835 raise common.ExternalError(
836 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700837 k = m.group(1)
838 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
839
Doug Zongkere05628c2009-08-20 17:38:42 -0700840 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800841 print("using:\n ", "\n ".join(mapped_keys))
842 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700843 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700844 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700845 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800846 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
847 if mapped_devkey != devkey:
848 misc_info["default_system_dev_certificate"] = mapped_devkey
849 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700850 print("META/otakeys.txt has no keys; using %s for OTA package"
851 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700852
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700853 # recovery now uses the same x509.pem version of the keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800854 # extra_recovery_keys are used only in recovery.
Tom Cherry2929cad2018-09-20 11:04:37 -0700855 if misc_info.get("recovery_as_boot") == "true":
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700856 recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
Tao Baoa80ed222016-06-16 14:41:24 -0700857 else:
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700858 recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
859
860 WriteOtacerts(output_tf_zip, recovery_keys_location,
861 mapped_keys + extra_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700862
863 # SystemUpdateActivity uses the x509.pem version of the keys, but
864 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800865 # We DO NOT include the extra_recovery_keys (if any) here.
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700866 WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700867
Tao Baoa80ed222016-06-16 14:41:24 -0700868 # For A/B devices, update the payload verification key.
869 if misc_info.get("ab_update") == "true":
870 # Unlike otacerts.zip that may contain multiple keys, we can only specify
871 # ONE payload verification key.
872 if len(mapped_keys) > 1:
873 print("\n WARNING: Found more than one OTA keys; Using the first one"
874 " as payload verification key.\n\n")
875
Tao Bao0c28d2d2017-12-24 10:37:38 -0800876 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800877 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700878 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700879 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700880 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
881 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700882 common.ZipWriteStr(
883 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700884 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700885 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700886
Tao Bao8adcfd12016-06-17 17:01:22 -0700887
Tao Bao0c28d2d2017-12-24 10:37:38 -0800888def ReplaceVerityPublicKey(output_zip, filename, key_path):
889 """Replaces the verity public key at the given path in the given zip.
890
891 Args:
892 output_zip: The output target_files zip.
893 filename: The archive name in the output zip.
894 key_path: The path to the public key.
895 """
896 print("Replacing verity public key with %s" % (key_path,))
897 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700898
Tao Bao8adcfd12016-06-17 17:01:22 -0700899
Tao Bao46a59992017-06-05 11:55:16 -0700900def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800901 """Replaces the verity private key in misc_info dict.
902
903 Args:
904 misc_info: The info dict.
905 key_path: The path to the private key in PKCS#8 format.
906 """
907 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700908 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700909
Tao Bao8adcfd12016-06-17 17:01:22 -0700910
Tao Baoe838d142017-12-23 23:44:48 -0800911def ReplaceVerityKeyId(input_zip, output_zip, key_path):
912 """Replaces the veritykeyid parameter in BOOT/cmdline.
913
914 Args:
915 input_zip: The input target_files zip, which should be already open.
916 output_zip: The output target_files zip, which should be already open and
917 writable.
918 key_path: The path to the PEM encoded X.509 certificate.
919 """
920 in_cmdline = input_zip.read("BOOT/cmdline")
921 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700922 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800923 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
924 return
925
Tao Bao0c28d2d2017-12-24 10:37:38 -0800926 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700927 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800928 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800929 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800930 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700931
Tao Baoe838d142017-12-23 23:44:48 -0800932 # Extract keyid using openssl command.
933 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800934 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800935 keyid, stderr = p.communicate()
936 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
937 keyid = re.search(
938 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
939 print("Replacing verity keyid with {}".format(keyid))
940 out_buffer.append("veritykeyid=id:%s" % (keyid,))
941
942 out_cmdline = ' '.join(out_buffer).strip() + '\n'
943 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700944
945
946def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
947 """Replaces META/misc_info.txt.
948
949 Only writes back the ones in the original META/misc_info.txt. Because the
950 current in-memory dict contains additional items computed at runtime.
951 """
952 misc_info_old = common.LoadDictionaryFromLines(
953 input_zip.read('META/misc_info.txt').split('\n'))
954 items = []
955 for key in sorted(misc_info):
956 if key in misc_info_old:
957 items.append('%s=%s' % (key, misc_info[key]))
958 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700959
Tao Bao8adcfd12016-06-17 17:01:22 -0700960
Tao Bao639118f2017-06-19 15:48:02 -0700961def ReplaceAvbSigningKeys(misc_info):
962 """Replaces the AVB signing keys."""
963
964 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800965 'boot' : 'avb_boot_add_hash_footer_args',
966 'dtbo' : 'avb_dtbo_add_hash_footer_args',
967 'recovery' : 'avb_recovery_add_hash_footer_args',
968 'system' : 'avb_system_add_hashtree_footer_args',
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800969 'system_other' : 'avb_system_other_add_hashtree_footer_args',
Tao Bao0c28d2d2017-12-24 10:37:38 -0800970 'vendor' : 'avb_vendor_add_hashtree_footer_args',
971 'vbmeta' : 'avb_vbmeta_args',
Tao Baod6085d62019-05-06 12:55:42 -0700972 'vbmeta_system' : 'avb_vbmeta_system_args',
973 'vbmeta_vendor' : 'avb_vbmeta_vendor_args',
Tao Bao639118f2017-06-19 15:48:02 -0700974 }
975
976 def ReplaceAvbPartitionSigningKey(partition):
977 key = OPTIONS.avb_keys.get(partition)
978 if not key:
979 return
980
981 algorithm = OPTIONS.avb_algorithms.get(partition)
982 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
983
Tao Bao0c28d2d2017-12-24 10:37:38 -0800984 print('Replacing AVB signing key for %s with "%s" (%s)' % (
985 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700986 misc_info['avb_' + partition + '_algorithm'] = algorithm
987 misc_info['avb_' + partition + '_key_path'] = key
988
989 extra_args = OPTIONS.avb_extra_args.get(partition)
990 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800991 print('Setting extra AVB signing args for %s to "%s"' % (
992 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700993 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
994 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
995
996 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
997 ReplaceAvbPartitionSigningKey(partition)
998
999
Doug Zongker831840e2011-09-22 10:28:04 -07001000def BuildKeyMap(misc_info, key_mapping_options):
1001 for s, d in key_mapping_options:
1002 if s is None: # -d option
1003 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07001004 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001005 devkeydir = os.path.dirname(devkey)
1006
1007 OPTIONS.key_map.update({
1008 devkeydir + "/testkey": d + "/releasekey",
1009 devkeydir + "/devkey": d + "/releasekey",
1010 devkeydir + "/media": d + "/media",
1011 devkeydir + "/shared": d + "/shared",
1012 devkeydir + "/platform": d + "/platform",
1013 })
1014 else:
1015 OPTIONS.key_map[s] = d
1016
1017
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001018def GetApiLevelAndCodename(input_tf_zip):
1019 data = input_tf_zip.read("SYSTEM/build.prop")
1020 api_level = None
1021 codename = None
1022 for line in data.split("\n"):
1023 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001024 if line and line[0] != '#' and "=" in line:
1025 key, value = line.split("=", 1)
1026 key = key.strip()
1027 if key == "ro.build.version.sdk":
1028 api_level = int(value.strip())
1029 elif key == "ro.build.version.codename":
1030 codename = value.strip()
1031
1032 if api_level is None:
1033 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1034 if codename is None:
1035 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1036
1037 return (api_level, codename)
1038
1039
1040def GetCodenameToApiLevelMap(input_tf_zip):
1041 data = input_tf_zip.read("SYSTEM/build.prop")
1042 api_level = None
1043 codenames = None
1044 for line in data.split("\n"):
1045 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001046 if line and line[0] != '#' and "=" in line:
1047 key, value = line.split("=", 1)
1048 key = key.strip()
1049 if key == "ro.build.version.sdk":
1050 api_level = int(value.strip())
1051 elif key == "ro.build.version.all_codenames":
1052 codenames = value.strip().split(",")
1053
1054 if api_level is None:
1055 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1056 if codenames is None:
1057 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1058
1059 result = dict()
1060 for codename in codenames:
1061 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001062 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001063 result[codename] = api_level
1064 return result
1065
1066
Tao Baoaa7e9932019-03-15 09:37:01 -07001067def ReadApexKeysInfo(tf_zip):
1068 """Parses the APEX keys info from a given target-files zip.
1069
1070 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1071 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1072 tuple of (payload_key, container_key).
1073
1074 Args:
1075 tf_zip: The input target_files ZipFile (already open).
1076
1077 Returns:
1078 (payload_key, container_key): payload_key contains the path to the payload
1079 signing key; container_key contains the path to the container signing
1080 key.
1081 """
1082 keys = {}
1083 for line in tf_zip.read("META/apexkeys.txt").split("\n"):
1084 line = line.strip()
1085 if not line:
1086 continue
1087 matches = re.match(
1088 r'^name="(?P<NAME>.*)"\s+'
1089 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1090 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1091 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
1092 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*)"$',
1093 line)
1094 if not matches:
1095 continue
1096
1097 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001098 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1099
1100 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1101 pubkey_suffix_len = len(pubkey_suffix)
1102 privkey_suffix_len = len(privkey_suffix)
1103 return (pubkey.endswith(pubkey_suffix) and
1104 privkey.endswith(privkey_suffix) and
1105 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1106
Tao Bao6d9e3da2019-03-26 12:59:25 -07001107 # Sanity check on the container key names, as we'll carry them without the
1108 # extensions. This doesn't apply to payload keys though, which we will use
1109 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001110 container_cert = matches.group("CONTAINER_CERT")
1111 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001112 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1113 container_key = 'PRESIGNED'
1114 elif CompareKeys(
Tao Baoaa7e9932019-03-15 09:37:01 -07001115 container_cert, OPTIONS.public_key_suffix,
1116 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001117 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1118 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001119 raise ValueError("Failed to parse container keys: \n{}".format(line))
1120
Tao Baof454c3a2019-04-24 23:53:42 -07001121 keys[name] = (payload_private_key, container_key)
Tao Baoaa7e9932019-03-15 09:37:01 -07001122
1123 return keys
1124
1125
Doug Zongkereef39442009-04-02 12:14:19 -07001126def main(argv):
1127
Doug Zongker831840e2011-09-22 10:28:04 -07001128 key_mapping_options = []
1129
Doug Zongkereef39442009-04-02 12:14:19 -07001130 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001131 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001132 names, key = a.split("=")
1133 names = names.split(",")
1134 for n in names:
1135 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001136 elif o == "--extra_apex_payload_key":
1137 apex_name, key = a.split("=")
1138 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001139 elif o == "--skip_apks_with_path_prefix":
1140 # Sanity check the prefix, which must be in all upper case.
1141 prefix = a.split('/')[0]
1142 if not prefix or prefix != prefix.upper():
1143 raise ValueError("Invalid path prefix '%s'" % (a,))
1144 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001145 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001146 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001147 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001148 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001149 elif o in ("-o", "--replace_ota_keys"):
1150 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001151 elif o in ("-t", "--tag_changes"):
1152 new = []
1153 for i in a.split(","):
1154 i = i.strip()
1155 if not i or i[0] not in "-+":
1156 raise ValueError("Bad tag change '%s'" % (i,))
1157 new.append(i[0] + i[1:].strip())
1158 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001159 elif o == "--replace_verity_public_key":
1160 OPTIONS.replace_verity_public_key = (True, a)
1161 elif o == "--replace_verity_private_key":
1162 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001163 elif o == "--replace_verity_keyid":
1164 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -07001165 elif o == "--avb_vbmeta_key":
1166 OPTIONS.avb_keys['vbmeta'] = a
1167 elif o == "--avb_vbmeta_algorithm":
1168 OPTIONS.avb_algorithms['vbmeta'] = a
1169 elif o == "--avb_vbmeta_extra_args":
1170 OPTIONS.avb_extra_args['vbmeta'] = a
1171 elif o == "--avb_boot_key":
1172 OPTIONS.avb_keys['boot'] = a
1173 elif o == "--avb_boot_algorithm":
1174 OPTIONS.avb_algorithms['boot'] = a
1175 elif o == "--avb_boot_extra_args":
1176 OPTIONS.avb_extra_args['boot'] = a
1177 elif o == "--avb_dtbo_key":
1178 OPTIONS.avb_keys['dtbo'] = a
1179 elif o == "--avb_dtbo_algorithm":
1180 OPTIONS.avb_algorithms['dtbo'] = a
1181 elif o == "--avb_dtbo_extra_args":
1182 OPTIONS.avb_extra_args['dtbo'] = a
1183 elif o == "--avb_system_key":
1184 OPTIONS.avb_keys['system'] = a
1185 elif o == "--avb_system_algorithm":
1186 OPTIONS.avb_algorithms['system'] = a
1187 elif o == "--avb_system_extra_args":
1188 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001189 elif o == "--avb_system_other_key":
1190 OPTIONS.avb_keys['system_other'] = a
1191 elif o == "--avb_system_other_algorithm":
1192 OPTIONS.avb_algorithms['system_other'] = a
1193 elif o == "--avb_system_other_extra_args":
1194 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001195 elif o == "--avb_vendor_key":
1196 OPTIONS.avb_keys['vendor'] = a
1197 elif o == "--avb_vendor_algorithm":
1198 OPTIONS.avb_algorithms['vendor'] = a
1199 elif o == "--avb_vendor_extra_args":
1200 OPTIONS.avb_extra_args['vendor'] = a
Tao Baod6085d62019-05-06 12:55:42 -07001201 elif o == "--avb_vbmeta_system_key":
1202 OPTIONS.avb_keys['vbmeta_system'] = a
1203 elif o == "--avb_vbmeta_system_algorithm":
1204 OPTIONS.avb_algorithms['vbmeta_system'] = a
1205 elif o == "--avb_vbmeta_system_extra_args":
1206 OPTIONS.avb_extra_args['vbmeta_system'] = a
1207 elif o == "--avb_vbmeta_vendor_key":
1208 OPTIONS.avb_keys['vbmeta_vendor'] = a
1209 elif o == "--avb_vbmeta_vendor_algorithm":
1210 OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1211 elif o == "--avb_vbmeta_vendor_extra_args":
1212 OPTIONS.avb_extra_args['vbmeta_vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001213 elif o == "--avb_apex_extra_args":
1214 OPTIONS.avb_extra_args['apex'] = a
Doug Zongkereef39442009-04-02 12:14:19 -07001215 else:
1216 return False
1217 return True
1218
Tao Bao639118f2017-06-19 15:48:02 -07001219 args = common.ParseOptions(
1220 argv, __doc__,
1221 extra_opts="e:d:k:ot:",
1222 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001223 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001224 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001225 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001226 "default_key_mappings=",
1227 "key_mapping=",
1228 "replace_ota_keys",
1229 "tag_changes=",
1230 "replace_verity_public_key=",
1231 "replace_verity_private_key=",
1232 "replace_verity_keyid=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001233 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001234 "avb_vbmeta_algorithm=",
1235 "avb_vbmeta_key=",
1236 "avb_vbmeta_extra_args=",
1237 "avb_boot_algorithm=",
1238 "avb_boot_key=",
1239 "avb_boot_extra_args=",
1240 "avb_dtbo_algorithm=",
1241 "avb_dtbo_key=",
1242 "avb_dtbo_extra_args=",
1243 "avb_system_algorithm=",
1244 "avb_system_key=",
1245 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001246 "avb_system_other_algorithm=",
1247 "avb_system_other_key=",
1248 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001249 "avb_vendor_algorithm=",
1250 "avb_vendor_key=",
1251 "avb_vendor_extra_args=",
Tao Baod6085d62019-05-06 12:55:42 -07001252 "avb_vbmeta_system_algorithm=",
1253 "avb_vbmeta_system_key=",
1254 "avb_vbmeta_system_extra_args=",
1255 "avb_vbmeta_vendor_algorithm=",
1256 "avb_vbmeta_vendor_key=",
1257 "avb_vbmeta_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -07001258 ],
1259 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001260
1261 if len(args) != 2:
1262 common.Usage(__doc__)
1263 sys.exit(1)
1264
Tao Baobadceb22019-03-15 09:33:43 -07001265 common.InitLogging()
1266
Doug Zongkereef39442009-04-02 12:14:19 -07001267 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -07001268 output_zip = zipfile.ZipFile(args[1], "w",
1269 compression=zipfile.ZIP_DEFLATED,
1270 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001271
Doug Zongker831840e2011-09-22 10:28:04 -07001272 misc_info = common.LoadInfoDict(input_zip)
1273
1274 BuildKeyMap(misc_info, key_mapping_options)
1275
Tao Baoaa7e9932019-03-15 09:37:01 -07001276 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1277 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001278
Tao Baoaa7e9932019-03-15 09:37:01 -07001279 apex_keys_info = ReadApexKeysInfo(input_zip)
1280 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1281
1282 CheckApkAndApexKeysAvailable(
1283 input_zip,
1284 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001285 compressed_extension,
1286 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001287
1288 key_passwords = common.GetKeyPasswords(
1289 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001290 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001291 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001292
Doug Zongker412c02f2014-02-13 10:58:24 -08001293 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001294 apk_keys, apex_keys, key_passwords,
1295 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001296 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001297
Tao Bao2ed665a2015-04-01 11:21:55 -07001298 common.ZipClose(input_zip)
1299 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001300
Tianjie Xub48589a2016-08-03 19:21:52 -07001301 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001302 new_args = ["--is_signing"]
1303 # add_img_to_target_files builds the system image from scratch, so the
1304 # recovery patch is guaranteed to be regenerated there.
1305 if OPTIONS.rebuild_recovery:
1306 new_args.append("--rebuild_recovery")
1307 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001308 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001309
Tao Bao0c28d2d2017-12-24 10:37:38 -08001310 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001311
1312
1313if __name__ == '__main__':
1314 try:
1315 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001316 except common.ExternalError as e:
1317 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001318 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -07001319 finally:
1320 common.Cleanup()