blob: d25ae0c0097704efd0e81573aea6b6f5748c96d9 [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",
Magnus Strandh234f4b42019-05-01 23:09:30 +0200561 "SYSTEM/vendor/build.prop",
562 "ODM/build.prop",
563 "VENDOR/odm/build.prop",
564 "PRODUCT/build.prop",
565 "SYSTEM/product/build.prop",
566 "PRODUCT_SERVICES/build.prop",
567 "SYSTEM/product_services/build.prop",
Tao Bao11f955c2018-06-19 12:19:35 -0700568 "SYSTEM/etc/prop.default",
569 "BOOT/RAMDISK/prop.default",
570 "BOOT/RAMDISK/default.prop", # legacy
571 "ROOT/default.prop", # legacy
572 "RECOVERY/RAMDISK/prop.default",
573 "RECOVERY/RAMDISK/default.prop"): # legacy
574 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800575 if stat.S_ISLNK(info.external_attr >> 16):
576 new_data = data
577 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800578 new_data = RewriteProps(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
Tao Bao66472632017-12-04 17:16:36 -0800581 # Replace the certs in *mac_permissions.xml (there could be multiple, such
582 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700583 elif filename.endswith("mac_permissions.xml"):
584 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400585 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700586 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700587
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700588 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700589 elif filename in ("SYSTEM/recovery-from-boot.p",
590 "SYSTEM/etc/recovery.img",
591 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700592 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700593
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700594 # Don't copy OTA certs if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700595 elif (
596 OPTIONS.replace_ota_keys and
597 filename in (
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700598 "BOOT/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700599 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700600 "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700601 "SYSTEM/etc/security/otacerts.zip",
602 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800603 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700604
Tao Bao46a59992017-06-05 11:55:16 -0700605 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700606 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700607 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700608
609 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700610 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700611 filename in ("BOOT/RAMDISK/verity_key",
612 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700613 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700614
Tao Bao8adcfd12016-06-17 17:01:22 -0700615 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700616 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700617 pass
618
Tianjie Xu4f099002016-08-11 18:04:27 -0700619 # Skip the care_map as we will regenerate the system/vendor images.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700620 elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700621 pass
622
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800623 # Updates system_other.avbpubkey in /product/etc/.
624 elif filename in (
625 "PRODUCT/etc/security/avb/system_other.avbpubkey",
626 "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
627 # Only update system_other's public key, if the corresponding signing
628 # key is specified via --avb_system_other_key.
629 signing_key = OPTIONS.avb_keys.get("system_other")
630 if signing_key:
631 public_key = common.ExtractAvbPublicKey(signing_key)
632 print(" Rewriting AVB public key of system_other in /product")
633 common.ZipWrite(output_tf_zip, public_key, filename)
634
Bowgo Tsai78369eb2019-04-23 12:28:44 +0800635 # Should NOT sign boot-debug.img.
636 elif filename in (
637 "BOOT/RAMDISK/force_debuggable",
638 "RECOVERY/RAMDISK/force_debuggable"
639 "RECOVERY/RAMDISK/first_stage_ramdisk/force_debuggable"):
640 raise common.ExternalError("debuggable boot.img cannot be signed")
641
Tao Baoa80ed222016-06-16 14:41:24 -0700642 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700643 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700644 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700645
Doug Zongker412c02f2014-02-13 10:58:24 -0800646 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700647 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800648
Tao Bao46a59992017-06-05 11:55:16 -0700649 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700650 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700651 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700652
653 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800654 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700655 # We are replacing the one in boot image only, since the one under
656 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700657 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700658 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700659
660 # Replace the keyid string in BOOT/cmdline.
661 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700662 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
663 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800664
Tao Bao639118f2017-06-19 15:48:02 -0700665 # Replace the AVB signing keys, if any.
666 ReplaceAvbSigningKeys(misc_info)
667
Tao Bao46a59992017-06-05 11:55:16 -0700668 # Write back misc_info with the latest values.
669 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
670
Doug Zongker8e931bf2009-04-06 15:21:45 -0700671
Robert Craig817c5742013-04-19 10:59:22 -0400672def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800673 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400674
Tao Bao66472632017-12-04 17:16:36 -0800675 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
676 be skipped. After the replacement, it additionally checks for duplicate
677 entries, which would otherwise fail the policy loading code in
678 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
679
680 Args:
681 data: Input string that contains a set of X.509 certs.
682
683 Returns:
684 A string after the replacement.
685
686 Raises:
687 AssertionError: On finding duplicate entries.
688 """
689 for old, new in OPTIONS.key_map.iteritems():
690 if OPTIONS.verbose:
691 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
692
693 try:
694 with open(old + ".x509.pem") as old_fp:
695 old_cert16 = base64.b16encode(
696 common.ParseCertificate(old_fp.read())).lower()
697 with open(new + ".x509.pem") as new_fp:
698 new_cert16 = base64.b16encode(
699 common.ParseCertificate(new_fp.read())).lower()
700 except IOError as e:
701 if OPTIONS.verbose or e.errno != errno.ENOENT:
702 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
703 "%s.x509.pem." % (e.filename, e.strerror, old, new))
704 continue
705
706 # Only match entire certs.
707 pattern = "\\b" + old_cert16 + "\\b"
708 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
709
710 if OPTIONS.verbose:
711 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
712 num, old, new))
713
714 # Verify that there're no duplicate entries after the replacement. Note that
715 # it's only checking entries with global seinfo at the moment (i.e. ignoring
716 # the ones with inner packages). (Bug: 69479366)
717 root = ElementTree.fromstring(data)
718 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
719 assert len(signatures) == len(set(signatures)), \
720 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400721
722 return data
723
724
Doug Zongkerc09abc82010-01-11 13:09:15 -0800725def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800726 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
727
728 Args:
729 tags: The input string that contains comma-separated tags.
730
731 Returns:
732 The updated tags (comma-separated and sorted).
733 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800734 tags = set(tags.split(","))
735 for ch in OPTIONS.tag_changes:
736 if ch[0] == "-":
737 tags.discard(ch[1:])
738 elif ch[0] == "+":
739 tags.add(ch[1:])
740 return ",".join(sorted(tags))
741
742
Tao Baoa7054ee2017-12-08 14:42:16 -0800743def RewriteProps(data):
744 """Rewrites the system properties in the given string.
745
746 Each property is expected in 'key=value' format. The properties that contain
747 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
748 EditTags().
749
750 Args:
751 data: Input string, separated by newlines.
752
753 Returns:
754 The string with modified properties.
755 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700756 output = []
757 for line in data.split("\n"):
758 line = line.strip()
759 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700760 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700761 key, value = line.split("=", 1)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200762 if (key.startswith("ro.") and
763 key.endswith((".build.fingerprint", ".build.thumbprint"))):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800764 pieces = value.split("/")
765 pieces[-1] = EditTags(pieces[-1])
766 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700767 elif key == "ro.bootimage.build.fingerprint":
768 pieces = value.split("/")
769 pieces[-1] = EditTags(pieces[-1])
770 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700771 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800772 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700773 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800774 pieces[-1] = EditTags(pieces[-1])
775 value = " ".join(pieces)
Magnus Strandh234f4b42019-05-01 23:09:30 +0200776 elif key.startswith("ro.") and key.endswith(".build.tags"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800777 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700778 elif key == "ro.build.display.id":
779 # change, eg, "JWR66N dev-keys" to "JWR66N"
780 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700781 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800782 value.pop()
783 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800784 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700785 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800786 print(" replace: ", original_line)
787 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700788 output.append(line)
789 return "\n".join(output) + "\n"
790
791
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700792def WriteOtacerts(output_zip, filename, keys):
793 """Constructs a zipfile from given keys; and writes it to output_zip.
794
795 Args:
796 output_zip: The output target_files zip.
797 filename: The archive name in the output zip.
798 keys: A list of public keys to use during OTA package verification.
799 """
800
801 try:
802 from StringIO import StringIO
803 except ImportError:
804 from io import StringIO
805 temp_file = StringIO()
806 certs_zip = zipfile.ZipFile(temp_file, "w")
807 for k in keys:
808 common.ZipWrite(certs_zip, k)
809 common.ZipClose(certs_zip)
810 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
811
812
Doug Zongker831840e2011-09-22 10:28:04 -0700813def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700814 try:
815 keylist = input_tf_zip.read("META/otakeys.txt").split()
816 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700817 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700818
Tao Baof718f902017-11-09 10:10:10 -0800819 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800820 if extra_recovery_keys:
821 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
822 for k in extra_recovery_keys.split()]
823 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800824 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800825 else:
826 extra_recovery_keys = []
827
Doug Zongker8e931bf2009-04-06 15:21:45 -0700828 mapped_keys = []
829 for k in keylist:
830 m = re.match(r"^(.*)\.x509\.pem$", k)
831 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800832 raise common.ExternalError(
833 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700834 k = m.group(1)
835 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
836
Doug Zongkere05628c2009-08-20 17:38:42 -0700837 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800838 print("using:\n ", "\n ".join(mapped_keys))
839 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700840 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700841 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700842 "build/make/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800843 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
844 if mapped_devkey != devkey:
845 misc_info["default_system_dev_certificate"] = mapped_devkey
846 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700847 print("META/otakeys.txt has no keys; using %s for OTA package"
848 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700849
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700850 # recovery now uses the same x509.pem version of the keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800851 # extra_recovery_keys are used only in recovery.
Tom Cherry2929cad2018-09-20 11:04:37 -0700852 if misc_info.get("recovery_as_boot") == "true":
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700853 recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
Tao Baoa80ed222016-06-16 14:41:24 -0700854 else:
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700855 recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
856
857 WriteOtacerts(output_tf_zip, recovery_keys_location,
858 mapped_keys + extra_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700859
860 # SystemUpdateActivity uses the x509.pem version of the keys, but
861 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800862 # We DO NOT include the extra_recovery_keys (if any) here.
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700863 WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700864
Tao Baoa80ed222016-06-16 14:41:24 -0700865 # For A/B devices, update the payload verification key.
866 if misc_info.get("ab_update") == "true":
867 # Unlike otacerts.zip that may contain multiple keys, we can only specify
868 # ONE payload verification key.
869 if len(mapped_keys) > 1:
870 print("\n WARNING: Found more than one OTA keys; Using the first one"
871 " as payload verification key.\n\n")
872
Tao Bao0c28d2d2017-12-24 10:37:38 -0800873 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800874 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700875 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700876 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700877 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
878 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700879 common.ZipWriteStr(
880 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700881 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700882 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700883
Tao Bao8adcfd12016-06-17 17:01:22 -0700884
Tao Bao0c28d2d2017-12-24 10:37:38 -0800885def ReplaceVerityPublicKey(output_zip, filename, key_path):
886 """Replaces the verity public key at the given path in the given zip.
887
888 Args:
889 output_zip: The output target_files zip.
890 filename: The archive name in the output zip.
891 key_path: The path to the public key.
892 """
893 print("Replacing verity public key with %s" % (key_path,))
894 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700895
Tao Bao8adcfd12016-06-17 17:01:22 -0700896
Tao Bao46a59992017-06-05 11:55:16 -0700897def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800898 """Replaces the verity private key in misc_info dict.
899
900 Args:
901 misc_info: The info dict.
902 key_path: The path to the private key in PKCS#8 format.
903 """
904 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700905 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700906
Tao Bao8adcfd12016-06-17 17:01:22 -0700907
Tao Baoe838d142017-12-23 23:44:48 -0800908def ReplaceVerityKeyId(input_zip, output_zip, key_path):
909 """Replaces the veritykeyid parameter in BOOT/cmdline.
910
911 Args:
912 input_zip: The input target_files zip, which should be already open.
913 output_zip: The output target_files zip, which should be already open and
914 writable.
915 key_path: The path to the PEM encoded X.509 certificate.
916 """
917 in_cmdline = input_zip.read("BOOT/cmdline")
918 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700919 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800920 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
921 return
922
Tao Bao0c28d2d2017-12-24 10:37:38 -0800923 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700924 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800925 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800926 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800927 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700928
Tao Baoe838d142017-12-23 23:44:48 -0800929 # Extract keyid using openssl command.
930 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800931 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800932 keyid, stderr = p.communicate()
933 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
934 keyid = re.search(
935 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
936 print("Replacing verity keyid with {}".format(keyid))
937 out_buffer.append("veritykeyid=id:%s" % (keyid,))
938
939 out_cmdline = ' '.join(out_buffer).strip() + '\n'
940 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700941
942
943def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
944 """Replaces META/misc_info.txt.
945
946 Only writes back the ones in the original META/misc_info.txt. Because the
947 current in-memory dict contains additional items computed at runtime.
948 """
949 misc_info_old = common.LoadDictionaryFromLines(
950 input_zip.read('META/misc_info.txt').split('\n'))
951 items = []
952 for key in sorted(misc_info):
953 if key in misc_info_old:
954 items.append('%s=%s' % (key, misc_info[key]))
955 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700956
Tao Bao8adcfd12016-06-17 17:01:22 -0700957
Tao Bao639118f2017-06-19 15:48:02 -0700958def ReplaceAvbSigningKeys(misc_info):
959 """Replaces the AVB signing keys."""
960
961 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800962 'boot' : 'avb_boot_add_hash_footer_args',
963 'dtbo' : 'avb_dtbo_add_hash_footer_args',
964 'recovery' : 'avb_recovery_add_hash_footer_args',
965 'system' : 'avb_system_add_hashtree_footer_args',
Bowgo Tsaie4544b12019-02-27 10:15:51 +0800966 'system_other' : 'avb_system_other_add_hashtree_footer_args',
Tao Bao0c28d2d2017-12-24 10:37:38 -0800967 'vendor' : 'avb_vendor_add_hashtree_footer_args',
968 'vbmeta' : 'avb_vbmeta_args',
Tao Bao639118f2017-06-19 15:48:02 -0700969 }
970
971 def ReplaceAvbPartitionSigningKey(partition):
972 key = OPTIONS.avb_keys.get(partition)
973 if not key:
974 return
975
976 algorithm = OPTIONS.avb_algorithms.get(partition)
977 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
978
Tao Bao0c28d2d2017-12-24 10:37:38 -0800979 print('Replacing AVB signing key for %s with "%s" (%s)' % (
980 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700981 misc_info['avb_' + partition + '_algorithm'] = algorithm
982 misc_info['avb_' + partition + '_key_path'] = key
983
984 extra_args = OPTIONS.avb_extra_args.get(partition)
985 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800986 print('Setting extra AVB signing args for %s to "%s"' % (
987 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700988 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
989 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
990
991 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
992 ReplaceAvbPartitionSigningKey(partition)
993
994
Doug Zongker831840e2011-09-22 10:28:04 -0700995def BuildKeyMap(misc_info, key_mapping_options):
996 for s, d in key_mapping_options:
997 if s is None: # -d option
998 devkey = misc_info.get("default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700999 "build/make/target/product/security/testkey")
Doug Zongker831840e2011-09-22 10:28:04 -07001000 devkeydir = os.path.dirname(devkey)
1001
1002 OPTIONS.key_map.update({
1003 devkeydir + "/testkey": d + "/releasekey",
1004 devkeydir + "/devkey": d + "/releasekey",
1005 devkeydir + "/media": d + "/media",
1006 devkeydir + "/shared": d + "/shared",
1007 devkeydir + "/platform": d + "/platform",
1008 })
1009 else:
1010 OPTIONS.key_map[s] = d
1011
1012
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001013def GetApiLevelAndCodename(input_tf_zip):
1014 data = input_tf_zip.read("SYSTEM/build.prop")
1015 api_level = None
1016 codename = None
1017 for line in data.split("\n"):
1018 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001019 if line and line[0] != '#' and "=" in line:
1020 key, value = line.split("=", 1)
1021 key = key.strip()
1022 if key == "ro.build.version.sdk":
1023 api_level = int(value.strip())
1024 elif key == "ro.build.version.codename":
1025 codename = value.strip()
1026
1027 if api_level is None:
1028 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1029 if codename is None:
1030 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1031
1032 return (api_level, codename)
1033
1034
1035def GetCodenameToApiLevelMap(input_tf_zip):
1036 data = input_tf_zip.read("SYSTEM/build.prop")
1037 api_level = None
1038 codenames = None
1039 for line in data.split("\n"):
1040 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001041 if line and line[0] != '#' and "=" in line:
1042 key, value = line.split("=", 1)
1043 key = key.strip()
1044 if key == "ro.build.version.sdk":
1045 api_level = int(value.strip())
1046 elif key == "ro.build.version.all_codenames":
1047 codenames = value.strip().split(",")
1048
1049 if api_level is None:
1050 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1051 if codenames is None:
1052 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1053
1054 result = dict()
1055 for codename in codenames:
1056 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001057 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001058 result[codename] = api_level
1059 return result
1060
1061
Tao Baoaa7e9932019-03-15 09:37:01 -07001062def ReadApexKeysInfo(tf_zip):
1063 """Parses the APEX keys info from a given target-files zip.
1064
1065 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1066 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1067 tuple of (payload_key, container_key).
1068
1069 Args:
1070 tf_zip: The input target_files ZipFile (already open).
1071
1072 Returns:
1073 (payload_key, container_key): payload_key contains the path to the payload
1074 signing key; container_key contains the path to the container signing
1075 key.
1076 """
1077 keys = {}
1078 for line in tf_zip.read("META/apexkeys.txt").split("\n"):
1079 line = line.strip()
1080 if not line:
1081 continue
1082 matches = re.match(
1083 r'^name="(?P<NAME>.*)"\s+'
1084 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1085 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1086 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
1087 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*)"$',
1088 line)
1089 if not matches:
1090 continue
1091
1092 name = matches.group('NAME')
Tao Baoaa7e9932019-03-15 09:37:01 -07001093 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1094
1095 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1096 pubkey_suffix_len = len(pubkey_suffix)
1097 privkey_suffix_len = len(privkey_suffix)
1098 return (pubkey.endswith(pubkey_suffix) and
1099 privkey.endswith(privkey_suffix) and
1100 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1101
Tao Bao6d9e3da2019-03-26 12:59:25 -07001102 # Sanity check on the container key names, as we'll carry them without the
1103 # extensions. This doesn't apply to payload keys though, which we will use
1104 # full names only.
Tao Baoaa7e9932019-03-15 09:37:01 -07001105 container_cert = matches.group("CONTAINER_CERT")
1106 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
Tao Baof454c3a2019-04-24 23:53:42 -07001107 if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1108 container_key = 'PRESIGNED'
1109 elif CompareKeys(
Tao Baoaa7e9932019-03-15 09:37:01 -07001110 container_cert, OPTIONS.public_key_suffix,
1111 container_private_key, OPTIONS.private_key_suffix):
Tao Baof454c3a2019-04-24 23:53:42 -07001112 container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1113 else:
Tao Baoaa7e9932019-03-15 09:37:01 -07001114 raise ValueError("Failed to parse container keys: \n{}".format(line))
1115
Tao Baof454c3a2019-04-24 23:53:42 -07001116 keys[name] = (payload_private_key, container_key)
Tao Baoaa7e9932019-03-15 09:37:01 -07001117
1118 return keys
1119
1120
Doug Zongkereef39442009-04-02 12:14:19 -07001121def main(argv):
1122
Doug Zongker831840e2011-09-22 10:28:04 -07001123 key_mapping_options = []
1124
Doug Zongkereef39442009-04-02 12:14:19 -07001125 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001126 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001127 names, key = a.split("=")
1128 names = names.split(",")
1129 for n in names:
1130 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001131 elif o == "--extra_apex_payload_key":
1132 apex_name, key = a.split("=")
1133 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001134 elif o == "--skip_apks_with_path_prefix":
1135 # Sanity check the prefix, which must be in all upper case.
1136 prefix = a.split('/')[0]
1137 if not prefix or prefix != prefix.upper():
1138 raise ValueError("Invalid path prefix '%s'" % (a,))
1139 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001140 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001141 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001142 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001143 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001144 elif o in ("-o", "--replace_ota_keys"):
1145 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001146 elif o in ("-t", "--tag_changes"):
1147 new = []
1148 for i in a.split(","):
1149 i = i.strip()
1150 if not i or i[0] not in "-+":
1151 raise ValueError("Bad tag change '%s'" % (i,))
1152 new.append(i[0] + i[1:].strip())
1153 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001154 elif o == "--replace_verity_public_key":
1155 OPTIONS.replace_verity_public_key = (True, a)
1156 elif o == "--replace_verity_private_key":
1157 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001158 elif o == "--replace_verity_keyid":
1159 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -07001160 elif o == "--avb_vbmeta_key":
1161 OPTIONS.avb_keys['vbmeta'] = a
1162 elif o == "--avb_vbmeta_algorithm":
1163 OPTIONS.avb_algorithms['vbmeta'] = a
1164 elif o == "--avb_vbmeta_extra_args":
1165 OPTIONS.avb_extra_args['vbmeta'] = a
1166 elif o == "--avb_boot_key":
1167 OPTIONS.avb_keys['boot'] = a
1168 elif o == "--avb_boot_algorithm":
1169 OPTIONS.avb_algorithms['boot'] = a
1170 elif o == "--avb_boot_extra_args":
1171 OPTIONS.avb_extra_args['boot'] = a
1172 elif o == "--avb_dtbo_key":
1173 OPTIONS.avb_keys['dtbo'] = a
1174 elif o == "--avb_dtbo_algorithm":
1175 OPTIONS.avb_algorithms['dtbo'] = a
1176 elif o == "--avb_dtbo_extra_args":
1177 OPTIONS.avb_extra_args['dtbo'] = a
1178 elif o == "--avb_system_key":
1179 OPTIONS.avb_keys['system'] = a
1180 elif o == "--avb_system_algorithm":
1181 OPTIONS.avb_algorithms['system'] = a
1182 elif o == "--avb_system_extra_args":
1183 OPTIONS.avb_extra_args['system'] = a
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001184 elif o == "--avb_system_other_key":
1185 OPTIONS.avb_keys['system_other'] = a
1186 elif o == "--avb_system_other_algorithm":
1187 OPTIONS.avb_algorithms['system_other'] = a
1188 elif o == "--avb_system_other_extra_args":
1189 OPTIONS.avb_extra_args['system_other'] = a
Tao Bao639118f2017-06-19 15:48:02 -07001190 elif o == "--avb_vendor_key":
1191 OPTIONS.avb_keys['vendor'] = a
1192 elif o == "--avb_vendor_algorithm":
1193 OPTIONS.avb_algorithms['vendor'] = a
1194 elif o == "--avb_vendor_extra_args":
1195 OPTIONS.avb_extra_args['vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001196 elif o == "--avb_apex_extra_args":
1197 OPTIONS.avb_extra_args['apex'] = a
Doug Zongkereef39442009-04-02 12:14:19 -07001198 else:
1199 return False
1200 return True
1201
Tao Bao639118f2017-06-19 15:48:02 -07001202 args = common.ParseOptions(
1203 argv, __doc__,
1204 extra_opts="e:d:k:ot:",
1205 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001206 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001207 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001208 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001209 "default_key_mappings=",
1210 "key_mapping=",
1211 "replace_ota_keys",
1212 "tag_changes=",
1213 "replace_verity_public_key=",
1214 "replace_verity_private_key=",
1215 "replace_verity_keyid=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001216 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001217 "avb_vbmeta_algorithm=",
1218 "avb_vbmeta_key=",
1219 "avb_vbmeta_extra_args=",
1220 "avb_boot_algorithm=",
1221 "avb_boot_key=",
1222 "avb_boot_extra_args=",
1223 "avb_dtbo_algorithm=",
1224 "avb_dtbo_key=",
1225 "avb_dtbo_extra_args=",
1226 "avb_system_algorithm=",
1227 "avb_system_key=",
1228 "avb_system_extra_args=",
Bowgo Tsaie4544b12019-02-27 10:15:51 +08001229 "avb_system_other_algorithm=",
1230 "avb_system_other_key=",
1231 "avb_system_other_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001232 "avb_vendor_algorithm=",
1233 "avb_vendor_key=",
1234 "avb_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -07001235 ],
1236 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001237
1238 if len(args) != 2:
1239 common.Usage(__doc__)
1240 sys.exit(1)
1241
Tao Baobadceb22019-03-15 09:33:43 -07001242 common.InitLogging()
1243
Doug Zongkereef39442009-04-02 12:14:19 -07001244 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -07001245 output_zip = zipfile.ZipFile(args[1], "w",
1246 compression=zipfile.ZIP_DEFLATED,
1247 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001248
Doug Zongker831840e2011-09-22 10:28:04 -07001249 misc_info = common.LoadInfoDict(input_zip)
1250
1251 BuildKeyMap(misc_info, key_mapping_options)
1252
Tao Baoaa7e9932019-03-15 09:37:01 -07001253 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1254 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001255
Tao Baoaa7e9932019-03-15 09:37:01 -07001256 apex_keys_info = ReadApexKeysInfo(input_zip)
1257 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1258
1259 CheckApkAndApexKeysAvailable(
1260 input_zip,
1261 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001262 compressed_extension,
1263 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001264
1265 key_passwords = common.GetKeyPasswords(
1266 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001267 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001268 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001269
Doug Zongker412c02f2014-02-13 10:58:24 -08001270 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001271 apk_keys, apex_keys, key_passwords,
1272 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001273 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001274
Tao Bao2ed665a2015-04-01 11:21:55 -07001275 common.ZipClose(input_zip)
1276 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001277
Tianjie Xub48589a2016-08-03 19:21:52 -07001278 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001279 new_args = ["--is_signing"]
1280 # add_img_to_target_files builds the system image from scratch, so the
1281 # recovery patch is guaranteed to be regenerated there.
1282 if OPTIONS.rebuild_recovery:
1283 new_args.append("--rebuild_recovery")
1284 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001285 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001286
Tao Bao0c28d2d2017-12-24 10:37:38 -08001287 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001288
1289
1290if __name__ == '__main__':
1291 try:
1292 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001293 except common.ExternalError as e:
1294 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001295 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -07001296 finally:
1297 common.Cleanup()