blob: 71598e341fbef9b92733eb003700161eaa221518 [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
59 META/misc_info.txt. (Defaulting to "build/target/product/security"
60 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
94 --avb_{boot,system,vendor,dtbo,vbmeta}_algorithm <algorithm>
95 --avb_{boot,system,vendor,dtbo,vbmeta}_key <key>
96 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
Tao Baoaa7e9932019-03-15 09:37:01 -070099 --avb_{apex,boot,system,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.
179 """
180 # Apply all the --extra_apex_payload_key options to override the payload
181 # signing keys in the given keys_info.
182 for apex, key in OPTIONS.extra_apex_payload_keys.items():
Tao Baoe1343992019-03-19 12:24:03 -0700183 if not key:
184 key = 'PRESIGNED'
Tao Baoaa7e9932019-03-15 09:37:01 -0700185 keys_info[apex] = (key, keys_info[apex][1])
186
187 # Apply the key remapping to container keys.
188 for apex, (payload_key, container_key) in keys_info.items():
189 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
190
191 # Apply all the --extra_apks options to override the container keys.
192 for apex, key in OPTIONS.extra_apks.items():
193 # Skip non-APEX containers.
194 if apex not in keys_info:
195 continue
Tao Baoe1343992019-03-19 12:24:03 -0700196 if not key:
197 key = 'PRESIGNED'
Tao Baofa9de0a2019-03-18 10:24:17 -0700198 keys_info[apex] = (keys_info[apex][0], key_map.get(key, key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700199
200 return keys_info
201
202
Tao Bao93c2a012018-06-19 12:19:35 -0700203def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700204 """Returns the APK info based on the given filename.
205
206 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700207 compressed extension into consideration. If it appears to be an APK file,
208 further checks if the APK file should be skipped when signing, based on the
209 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700210
211 Args:
212 filename: Path to the file.
213 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
214 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700215 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700216
217 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700218 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
219 given filename is an APK file. is_compressed indicates whether the APK file
220 is compressed (only meaningful when is_apk is True). should_be_skipped
221 indicates whether the filename matches any of the given prefixes to be
222 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700223
224 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700225 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700226 """
227 assert compressed_extension is None or compressed_extension.startswith('.'), \
228 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
229
Tao Bao93c2a012018-06-19 12:19:35 -0700230 # skipped_prefixes should be one of set/list/tuple types. Other types such as
231 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700232 assert isinstance(skipped_prefixes, (set, list, tuple)), \
233 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700234
Tao Bao11f955c2018-06-19 12:19:35 -0700235 compressed_apk_extension = (
236 ".apk" + compressed_extension if compressed_extension else None)
237 is_apk = (filename.endswith(".apk") or
238 (compressed_apk_extension and
239 filename.endswith(compressed_apk_extension)))
240 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700241 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700242
243 is_compressed = (compressed_apk_extension and
244 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700245 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
246 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700247
248
Tao Baoaa7e9932019-03-15 09:37:01 -0700249def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
Tao Baoe1343992019-03-19 12:24:03 -0700250 compressed_extension, apex_keys):
Tao Baoaa7e9932019-03-15 09:37:01 -0700251 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700252
253 Args:
254 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700255 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700256 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700257 '.gz', or None if there's no compressed APKs.
Tao Baoe1343992019-03-19 12:24:03 -0700258 apex_keys: A dict that contains the key mapping from APEX name to
259 (payload_key, container_key).
Tao Bao11f955c2018-06-19 12:19:35 -0700260
261 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700262 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700263 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700264 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700265 for info in input_tf_zip.infolist():
Tao Baoaa7e9932019-03-15 09:37:01 -0700266 # Handle APEXes first, e.g. SYSTEM/apex/com.android.tzdata.apex.
267 if (info.filename.startswith('SYSTEM/apex') and
268 info.filename.endswith('.apex')):
269 name = os.path.basename(info.filename)
270 if name not in known_keys:
271 unknown_files.append(name)
272 continue
273
274 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700275 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
276 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
277 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700278 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700279
Tao Bao11f955c2018-06-19 12:19:35 -0700280 name = os.path.basename(info.filename)
281 if is_compressed:
282 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700283 if name not in known_keys:
284 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700285
Tao Baoaa7e9932019-03-15 09:37:01 -0700286 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700287 ("No key specified for:\n {}\n"
288 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700289 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700290
Tao Baoe1343992019-03-19 12:24:03 -0700291 # For all the APEXes, double check that we won't have an APEX that has only
292 # one of the payload / container keys set.
293 if not apex_keys:
294 return
295
296 invalid_apexes = []
297 for info in input_tf_zip.infolist():
298 if (not info.filename.startswith('SYSTEM/apex') or
299 not info.filename.endswith('.apex')):
300 continue
301
302 name = os.path.basename(info.filename)
303 (payload_key, container_key) = apex_keys[name]
304 if ((payload_key in common.SPECIAL_CERT_STRINGS and
305 container_key not in common.SPECIAL_CERT_STRINGS) or
306 (payload_key not in common.SPECIAL_CERT_STRINGS and
307 container_key in common.SPECIAL_CERT_STRINGS)):
308 invalid_apexes.append(
309 "{}: payload_key {}, container_key {}".format(
310 name, payload_key, container_key))
311
312 assert not invalid_apexes, \
313 "Invalid APEX keys specified:\n {}\n".format(
314 "\n ".join(invalid_apexes))
315
Doug Zongkereb338ef2009-05-20 16:50:49 -0700316
Narayan Kamatha07bf042017-08-14 14:49:21 +0100317def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
318 is_compressed):
Doug Zongkereef39442009-04-02 12:14:19 -0700319 unsigned = tempfile.NamedTemporaryFile()
320 unsigned.write(data)
321 unsigned.flush()
322
Narayan Kamatha07bf042017-08-14 14:49:21 +0100323 if is_compressed:
324 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800325 with gzip.open(unsigned.name, "rb") as in_file, \
326 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100327 shutil.copyfileobj(in_file, out_file)
328
329 # Finally, close the "unsigned" file (which is gzip compressed), and then
330 # replace it with the uncompressed version.
331 #
332 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
333 # we could just gzip / gunzip in-memory buffers instead.
334 unsigned.close()
335 unsigned = uncompressed
336
Doug Zongkereef39442009-04-02 12:14:19 -0700337 signed = tempfile.NamedTemporaryFile()
338
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800339 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
340 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
341 # didn't change, we don't want its signature to change due to the switch
342 # from SHA-1 to SHA-256.
343 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
344 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
345 # that the APK's minSdkVersion is 1.
346 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
347 # determine whether to use SHA-256.
348 min_api_level = None
349 if platform_api_level > 23:
350 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
351 # minSdkVersion attribute
352 min_api_level = None
353 else:
354 # Force APK signer to use SHA-1
355 min_api_level = 1
356
357 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800358 min_api_level=min_api_level,
359 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700360
Tao Bao0c28d2d2017-12-24 10:37:38 -0800361 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100362 if is_compressed:
363 # Recompress the file after it has been signed.
364 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800365 with open(signed.name, "rb") as in_file, \
366 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100367 shutil.copyfileobj(in_file, out_file)
368
369 data = compressed.read()
370 compressed.close()
371 else:
372 data = signed.read()
373
Doug Zongkereef39442009-04-02 12:14:19 -0700374 unsigned.close()
375 signed.close()
376
377 return data
378
379
Tao Baoaa7e9932019-03-15 09:37:01 -0700380def SignApex(apex_data, payload_key, container_key, container_pw,
381 codename_to_api_level_map, signing_args=None):
382 """Signs the current APEX with the given payload/container keys.
383
384 Args:
385 apex_data: Raw APEX data.
386 payload_key: The path to payload signing key (w/o extension).
387 container_key: The path to container signing key (w/o extension).
388 container_pw: The matching password of the container_key, or None.
389 codename_to_api_level_map: A dict that maps from codename to API level.
390 signing_args: Additional args to be passed to the payload signer.
391
392 Returns:
393 (signed_apex, payload_key_name): signed_apex is the path to the signed APEX
394 file; payload_key_name is a str of the payload signing key name (e.g.
395 com.android.tzdata).
396 """
397 apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
398 with open(apex_file, 'wb') as apex_fp:
399 apex_fp.write(apex_data)
400
401 APEX_PAYLOAD_IMAGE = 'apex_payload.img'
402
403 # Signing an APEX is a two step process.
404 # 1. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given payload_key.
405 payload_dir = common.MakeTempDir(prefix='apex-payload-')
406 with zipfile.ZipFile(apex_file) as apex_fd:
407 payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
408
409 payload_info = apex_utils.ParseApexPayloadInfo(payload_file)
410 apex_utils.SignApexPayload(
411 payload_file,
412 payload_key,
413 payload_info['apex.key'],
414 payload_info['Algorithm'],
415 payload_info['Salt'],
416 signing_args)
417
418 common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
419 apex_zip = zipfile.ZipFile(apex_file, 'a')
420 common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
421 common.ZipClose(apex_zip)
422
423 # 2. Sign the overall APEX container with container_key.
424 signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
425 common.SignFile(
426 apex_file,
427 signed_apex,
428 container_key,
429 container_pw,
430 codename_to_api_level_map=codename_to_api_level_map)
431
432 signed_and_aligned_apex = common.MakeTempFile(
433 prefix='apex-container-', suffix='.apex')
434 common.RunAndCheckOutput(
435 ['zipalign', '-f', '4096', signed_apex, signed_and_aligned_apex])
436
437 return (signed_and_aligned_apex, payload_info['apex.key'])
438
439
Doug Zongker412c02f2014-02-13 10:58:24 -0800440def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700441 apk_keys, apex_keys, key_passwords,
442 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100443 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700444 # maxsize measures the maximum filename length, including the ones to be
445 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800446 maxsize = max(
447 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700448 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700449 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800450
Tao Baoaa7e9932019-03-15 09:37:01 -0700451 # A dict of APEX payload public keys that should be updated, i.e. the files
452 # under '/system/etc/security/apex/'.
453 updated_apex_payload_keys = {}
454
Doug Zongkereef39442009-04-02 12:14:19 -0700455 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700456 filename = info.filename
457 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700458 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700459
Tao Bao33bf2682019-01-11 12:37:35 -0800460 # Skip split super images, which will be re-generated during signing.
461 if filename.startswith("OTA/") and filename.endswith(".img"):
462 continue
463
Tao Bao11f955c2018-06-19 12:19:35 -0700464 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700465 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700466 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
467 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
468
469 if is_apk and should_be_skipped:
470 # Copy skipped APKs verbatim.
471 print(
472 "NOT signing: %s\n"
473 " (skipped due to matching prefix)" % (filename,))
474 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800475
Tao Baof2cffbd2015-07-22 12:33:18 -0700476 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700477 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700478 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100479 if is_compressed:
480 name = name[:-len(compressed_extension)]
481
Tao Baoaa7e9932019-03-15 09:37:01 -0700482 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800483 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800484 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800485 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800486 codename_to_api_level_map, is_compressed)
Tao Bao2ed665a2015-04-01 11:21:55 -0700487 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700488 else:
489 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700490 print(
491 "NOT signing: %s\n"
492 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700493 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700494
Tao Baoaa7e9932019-03-15 09:37:01 -0700495 # Sign bundled APEX files.
496 elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
497 name = os.path.basename(filename)
498 payload_key, container_key = apex_keys[name]
499
Tao Baoe1343992019-03-19 12:24:03 -0700500 # We've asserted not having a case with only one of them PRESIGNED.
501 if (payload_key not in common.SPECIAL_CERT_STRINGS and
502 container_key not in common.SPECIAL_CERT_STRINGS):
503 print(" signing: %-*s container (%s)" % (
504 maxsize, name, container_key))
505 print(" : %-*s payload (%s)" % (
506 maxsize, name, payload_key))
Tao Baoaa7e9932019-03-15 09:37:01 -0700507
Tao Baoe1343992019-03-19 12:24:03 -0700508 (signed_apex, payload_key_name) = SignApex(
509 data,
510 payload_key,
511 container_key,
512 key_passwords[container_key],
513 codename_to_api_level_map,
514 OPTIONS.avb_extra_args.get('apex'))
515 common.ZipWrite(output_tf_zip, signed_apex, filename)
516 updated_apex_payload_keys[payload_key_name] = payload_key
Tao Baoaa7e9932019-03-15 09:37:01 -0700517
Tao Baoe1343992019-03-19 12:24:03 -0700518 else:
519 print(
520 "NOT signing: %s\n"
521 " (skipped due to special cert string)" % (name,))
522 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoaa7e9932019-03-15 09:37:01 -0700523
524 # AVB public keys for the installed APEXes, which will be updated later.
525 elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
526 filename != 'SYSTEM/etc/security/apex/'):
527 continue
528
Tao Baoa80ed222016-06-16 14:41:24 -0700529 # System properties.
Tao Bao11f955c2018-06-19 12:19:35 -0700530 elif filename in ("SYSTEM/build.prop",
531 "VENDOR/build.prop",
532 "SYSTEM/etc/prop.default",
533 "BOOT/RAMDISK/prop.default",
534 "BOOT/RAMDISK/default.prop", # legacy
535 "ROOT/default.prop", # legacy
536 "RECOVERY/RAMDISK/prop.default",
537 "RECOVERY/RAMDISK/default.prop"): # legacy
538 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800539 if stat.S_ISLNK(info.external_attr >> 16):
540 new_data = data
541 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800542 new_data = RewriteProps(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700543 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700544
Tao Bao66472632017-12-04 17:16:36 -0800545 # Replace the certs in *mac_permissions.xml (there could be multiple, such
546 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700547 elif filename.endswith("mac_permissions.xml"):
548 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400549 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700550 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700551
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700552 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700553 elif filename in ("SYSTEM/recovery-from-boot.p",
554 "SYSTEM/etc/recovery.img",
555 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700556 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700557
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700558 # Don't copy OTA certs if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700559 elif (
560 OPTIONS.replace_ota_keys and
561 filename in (
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700562 "BOOT/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700563 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700564 "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700565 "SYSTEM/etc/security/otacerts.zip",
566 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800567 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700568
Tao Bao46a59992017-06-05 11:55:16 -0700569 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700570 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700571 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700572
573 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700574 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700575 filename in ("BOOT/RAMDISK/verity_key",
576 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700577 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700578
Tao Bao8adcfd12016-06-17 17:01:22 -0700579 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700580 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700581 pass
582
Tianjie Xu4f099002016-08-11 18:04:27 -0700583 # Skip the care_map as we will regenerate the system/vendor images.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700584 elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700585 pass
586
Tao Baoa80ed222016-06-16 14:41:24 -0700587 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700588 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700589 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700590
Tao Baoaa7e9932019-03-15 09:37:01 -0700591 # Update APEX payload public keys.
592 for info in input_tf_zip.infolist():
593 filename = info.filename
594 if (os.path.dirname(filename) != 'SYSTEM/etc/security/apex' or
595 filename == 'SYSTEM/etc/security/apex/'):
596 continue
597
598 name = os.path.basename(filename)
Tao Baoe1343992019-03-19 12:24:03 -0700599
600 # Skip PRESIGNED APEXes.
601 if name not in updated_apex_payload_keys:
602 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700603
604 key_path = updated_apex_payload_keys[name]
605 if not os.path.exists(key_path) and not key_path.endswith('.pem'):
606 key_path = '{}.pem'.format(key_path)
607 assert os.path.exists(key_path), \
608 'Failed to find public key file {} for APEX {}'.format(
609 updated_apex_payload_keys[name], name)
610
611 print('Replacing APEX payload public key for {} with {}'.format(
612 name, key_path))
613
614 public_key = common.ExtractAvbPublicKey(key_path)
615 common.ZipWrite(output_tf_zip, public_key, arcname=filename)
616
Doug Zongker412c02f2014-02-13 10:58:24 -0800617 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700618 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800619
Tao Bao46a59992017-06-05 11:55:16 -0700620 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700621 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700622 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700623
624 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800625 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700626 # We are replacing the one in boot image only, since the one under
627 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700628 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700629 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700630
631 # Replace the keyid string in BOOT/cmdline.
632 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700633 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
634 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800635
Tao Bao639118f2017-06-19 15:48:02 -0700636 # Replace the AVB signing keys, if any.
637 ReplaceAvbSigningKeys(misc_info)
638
Tao Bao46a59992017-06-05 11:55:16 -0700639 # Write back misc_info with the latest values.
640 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
641
Doug Zongker8e931bf2009-04-06 15:21:45 -0700642
Robert Craig817c5742013-04-19 10:59:22 -0400643def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800644 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400645
Tao Bao66472632017-12-04 17:16:36 -0800646 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
647 be skipped. After the replacement, it additionally checks for duplicate
648 entries, which would otherwise fail the policy loading code in
649 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
650
651 Args:
652 data: Input string that contains a set of X.509 certs.
653
654 Returns:
655 A string after the replacement.
656
657 Raises:
658 AssertionError: On finding duplicate entries.
659 """
660 for old, new in OPTIONS.key_map.iteritems():
661 if OPTIONS.verbose:
662 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
663
664 try:
665 with open(old + ".x509.pem") as old_fp:
666 old_cert16 = base64.b16encode(
667 common.ParseCertificate(old_fp.read())).lower()
668 with open(new + ".x509.pem") as new_fp:
669 new_cert16 = base64.b16encode(
670 common.ParseCertificate(new_fp.read())).lower()
671 except IOError as e:
672 if OPTIONS.verbose or e.errno != errno.ENOENT:
673 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
674 "%s.x509.pem." % (e.filename, e.strerror, old, new))
675 continue
676
677 # Only match entire certs.
678 pattern = "\\b" + old_cert16 + "\\b"
679 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
680
681 if OPTIONS.verbose:
682 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
683 num, old, new))
684
685 # Verify that there're no duplicate entries after the replacement. Note that
686 # it's only checking entries with global seinfo at the moment (i.e. ignoring
687 # the ones with inner packages). (Bug: 69479366)
688 root = ElementTree.fromstring(data)
689 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
690 assert len(signatures) == len(set(signatures)), \
691 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400692
693 return data
694
695
Doug Zongkerc09abc82010-01-11 13:09:15 -0800696def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800697 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
698
699 Args:
700 tags: The input string that contains comma-separated tags.
701
702 Returns:
703 The updated tags (comma-separated and sorted).
704 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800705 tags = set(tags.split(","))
706 for ch in OPTIONS.tag_changes:
707 if ch[0] == "-":
708 tags.discard(ch[1:])
709 elif ch[0] == "+":
710 tags.add(ch[1:])
711 return ",".join(sorted(tags))
712
713
Tao Baoa7054ee2017-12-08 14:42:16 -0800714def RewriteProps(data):
715 """Rewrites the system properties in the given string.
716
717 Each property is expected in 'key=value' format. The properties that contain
718 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
719 EditTags().
720
721 Args:
722 data: Input string, separated by newlines.
723
724 Returns:
725 The string with modified properties.
726 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700727 output = []
728 for line in data.split("\n"):
729 line = line.strip()
730 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700731 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700732 key, value = line.split("=", 1)
Tao Baoa7054ee2017-12-08 14:42:16 -0800733 if key in ("ro.build.fingerprint", "ro.build.thumbprint",
734 "ro.vendor.build.fingerprint", "ro.vendor.build.thumbprint"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800735 pieces = value.split("/")
736 pieces[-1] = EditTags(pieces[-1])
737 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700738 elif key == "ro.bootimage.build.fingerprint":
739 pieces = value.split("/")
740 pieces[-1] = EditTags(pieces[-1])
741 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700742 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800743 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700744 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800745 pieces[-1] = EditTags(pieces[-1])
746 value = " ".join(pieces)
747 elif key == "ro.build.tags":
748 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700749 elif key == "ro.build.display.id":
750 # change, eg, "JWR66N dev-keys" to "JWR66N"
751 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700752 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800753 value.pop()
754 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800755 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700756 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800757 print(" replace: ", original_line)
758 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700759 output.append(line)
760 return "\n".join(output) + "\n"
761
762
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700763def WriteOtacerts(output_zip, filename, keys):
764 """Constructs a zipfile from given keys; and writes it to output_zip.
765
766 Args:
767 output_zip: The output target_files zip.
768 filename: The archive name in the output zip.
769 keys: A list of public keys to use during OTA package verification.
770 """
771
772 try:
773 from StringIO import StringIO
774 except ImportError:
775 from io import StringIO
776 temp_file = StringIO()
777 certs_zip = zipfile.ZipFile(temp_file, "w")
778 for k in keys:
779 common.ZipWrite(certs_zip, k)
780 common.ZipClose(certs_zip)
781 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
782
783
Doug Zongker831840e2011-09-22 10:28:04 -0700784def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700785 try:
786 keylist = input_tf_zip.read("META/otakeys.txt").split()
787 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700788 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700789
Tao Baof718f902017-11-09 10:10:10 -0800790 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800791 if extra_recovery_keys:
792 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
793 for k in extra_recovery_keys.split()]
794 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800795 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800796 else:
797 extra_recovery_keys = []
798
Doug Zongker8e931bf2009-04-06 15:21:45 -0700799 mapped_keys = []
800 for k in keylist:
801 m = re.match(r"^(.*)\.x509\.pem$", k)
802 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800803 raise common.ExternalError(
804 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700805 k = m.group(1)
806 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
807
Doug Zongkere05628c2009-08-20 17:38:42 -0700808 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800809 print("using:\n ", "\n ".join(mapped_keys))
810 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700811 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700812 devkey = misc_info.get("default_system_dev_certificate",
813 "build/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800814 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
815 if mapped_devkey != devkey:
816 misc_info["default_system_dev_certificate"] = mapped_devkey
817 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700818 print("META/otakeys.txt has no keys; using %s for OTA package"
819 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700820
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700821 # recovery now uses the same x509.pem version of the keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800822 # extra_recovery_keys are used only in recovery.
Tom Cherry2929cad2018-09-20 11:04:37 -0700823 if misc_info.get("recovery_as_boot") == "true":
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700824 recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
Tao Baoa80ed222016-06-16 14:41:24 -0700825 else:
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700826 recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
827
828 WriteOtacerts(output_tf_zip, recovery_keys_location,
829 mapped_keys + extra_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700830
831 # SystemUpdateActivity uses the x509.pem version of the keys, but
832 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800833 # We DO NOT include the extra_recovery_keys (if any) here.
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700834 WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700835
Tao Baoa80ed222016-06-16 14:41:24 -0700836 # For A/B devices, update the payload verification key.
837 if misc_info.get("ab_update") == "true":
838 # Unlike otacerts.zip that may contain multiple keys, we can only specify
839 # ONE payload verification key.
840 if len(mapped_keys) > 1:
841 print("\n WARNING: Found more than one OTA keys; Using the first one"
842 " as payload verification key.\n\n")
843
Tao Bao0c28d2d2017-12-24 10:37:38 -0800844 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800845 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700846 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700847 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700848 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
849 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700850 common.ZipWriteStr(
851 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700852 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700853 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700854
Tao Bao8adcfd12016-06-17 17:01:22 -0700855
Tao Bao0c28d2d2017-12-24 10:37:38 -0800856def ReplaceVerityPublicKey(output_zip, filename, key_path):
857 """Replaces the verity public key at the given path in the given zip.
858
859 Args:
860 output_zip: The output target_files zip.
861 filename: The archive name in the output zip.
862 key_path: The path to the public key.
863 """
864 print("Replacing verity public key with %s" % (key_path,))
865 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700866
Tao Bao8adcfd12016-06-17 17:01:22 -0700867
Tao Bao46a59992017-06-05 11:55:16 -0700868def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800869 """Replaces the verity private key in misc_info dict.
870
871 Args:
872 misc_info: The info dict.
873 key_path: The path to the private key in PKCS#8 format.
874 """
875 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700876 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700877
Tao Bao8adcfd12016-06-17 17:01:22 -0700878
Tao Baoe838d142017-12-23 23:44:48 -0800879def ReplaceVerityKeyId(input_zip, output_zip, key_path):
880 """Replaces the veritykeyid parameter in BOOT/cmdline.
881
882 Args:
883 input_zip: The input target_files zip, which should be already open.
884 output_zip: The output target_files zip, which should be already open and
885 writable.
886 key_path: The path to the PEM encoded X.509 certificate.
887 """
888 in_cmdline = input_zip.read("BOOT/cmdline")
889 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700890 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800891 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
892 return
893
Tao Bao0c28d2d2017-12-24 10:37:38 -0800894 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700895 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800896 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800897 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800898 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700899
Tao Baoe838d142017-12-23 23:44:48 -0800900 # Extract keyid using openssl command.
901 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800902 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800903 keyid, stderr = p.communicate()
904 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
905 keyid = re.search(
906 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
907 print("Replacing verity keyid with {}".format(keyid))
908 out_buffer.append("veritykeyid=id:%s" % (keyid,))
909
910 out_cmdline = ' '.join(out_buffer).strip() + '\n'
911 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700912
913
914def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
915 """Replaces META/misc_info.txt.
916
917 Only writes back the ones in the original META/misc_info.txt. Because the
918 current in-memory dict contains additional items computed at runtime.
919 """
920 misc_info_old = common.LoadDictionaryFromLines(
921 input_zip.read('META/misc_info.txt').split('\n'))
922 items = []
923 for key in sorted(misc_info):
924 if key in misc_info_old:
925 items.append('%s=%s' % (key, misc_info[key]))
926 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700927
Tao Bao8adcfd12016-06-17 17:01:22 -0700928
Tao Bao639118f2017-06-19 15:48:02 -0700929def ReplaceAvbSigningKeys(misc_info):
930 """Replaces the AVB signing keys."""
931
932 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800933 'boot' : 'avb_boot_add_hash_footer_args',
934 'dtbo' : 'avb_dtbo_add_hash_footer_args',
935 'recovery' : 'avb_recovery_add_hash_footer_args',
936 'system' : 'avb_system_add_hashtree_footer_args',
937 'vendor' : 'avb_vendor_add_hashtree_footer_args',
938 'vbmeta' : 'avb_vbmeta_args',
Tao Bao639118f2017-06-19 15:48:02 -0700939 }
940
941 def ReplaceAvbPartitionSigningKey(partition):
942 key = OPTIONS.avb_keys.get(partition)
943 if not key:
944 return
945
946 algorithm = OPTIONS.avb_algorithms.get(partition)
947 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
948
Tao Bao0c28d2d2017-12-24 10:37:38 -0800949 print('Replacing AVB signing key for %s with "%s" (%s)' % (
950 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700951 misc_info['avb_' + partition + '_algorithm'] = algorithm
952 misc_info['avb_' + partition + '_key_path'] = key
953
954 extra_args = OPTIONS.avb_extra_args.get(partition)
955 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800956 print('Setting extra AVB signing args for %s to "%s"' % (
957 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700958 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
959 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
960
961 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
962 ReplaceAvbPartitionSigningKey(partition)
963
964
Doug Zongker831840e2011-09-22 10:28:04 -0700965def BuildKeyMap(misc_info, key_mapping_options):
966 for s, d in key_mapping_options:
967 if s is None: # -d option
968 devkey = misc_info.get("default_system_dev_certificate",
969 "build/target/product/security/testkey")
970 devkeydir = os.path.dirname(devkey)
971
972 OPTIONS.key_map.update({
973 devkeydir + "/testkey": d + "/releasekey",
974 devkeydir + "/devkey": d + "/releasekey",
975 devkeydir + "/media": d + "/media",
976 devkeydir + "/shared": d + "/shared",
977 devkeydir + "/platform": d + "/platform",
978 })
979 else:
980 OPTIONS.key_map[s] = d
981
982
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800983def GetApiLevelAndCodename(input_tf_zip):
984 data = input_tf_zip.read("SYSTEM/build.prop")
985 api_level = None
986 codename = None
987 for line in data.split("\n"):
988 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800989 if line and line[0] != '#' and "=" in line:
990 key, value = line.split("=", 1)
991 key = key.strip()
992 if key == "ro.build.version.sdk":
993 api_level = int(value.strip())
994 elif key == "ro.build.version.codename":
995 codename = value.strip()
996
997 if api_level is None:
998 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
999 if codename is None:
1000 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1001
1002 return (api_level, codename)
1003
1004
1005def GetCodenameToApiLevelMap(input_tf_zip):
1006 data = input_tf_zip.read("SYSTEM/build.prop")
1007 api_level = None
1008 codenames = None
1009 for line in data.split("\n"):
1010 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001011 if line and line[0] != '#' and "=" in line:
1012 key, value = line.split("=", 1)
1013 key = key.strip()
1014 if key == "ro.build.version.sdk":
1015 api_level = int(value.strip())
1016 elif key == "ro.build.version.all_codenames":
1017 codenames = value.strip().split(",")
1018
1019 if api_level is None:
1020 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1021 if codenames is None:
1022 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1023
1024 result = dict()
1025 for codename in codenames:
1026 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -07001027 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001028 result[codename] = api_level
1029 return result
1030
1031
Tao Baoaa7e9932019-03-15 09:37:01 -07001032def ReadApexKeysInfo(tf_zip):
1033 """Parses the APEX keys info from a given target-files zip.
1034
1035 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1036 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1037 tuple of (payload_key, container_key).
1038
1039 Args:
1040 tf_zip: The input target_files ZipFile (already open).
1041
1042 Returns:
1043 (payload_key, container_key): payload_key contains the path to the payload
1044 signing key; container_key contains the path to the container signing
1045 key.
1046 """
1047 keys = {}
1048 for line in tf_zip.read("META/apexkeys.txt").split("\n"):
1049 line = line.strip()
1050 if not line:
1051 continue
1052 matches = re.match(
1053 r'^name="(?P<NAME>.*)"\s+'
1054 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1055 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1056 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
1057 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*)"$',
1058 line)
1059 if not matches:
1060 continue
1061
1062 name = matches.group('NAME')
1063 payload_public_key = matches.group("PAYLOAD_PUBLIC_KEY")
1064 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1065
1066 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1067 pubkey_suffix_len = len(pubkey_suffix)
1068 privkey_suffix_len = len(privkey_suffix)
1069 return (pubkey.endswith(pubkey_suffix) and
1070 privkey.endswith(privkey_suffix) and
1071 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1072
1073 PAYLOAD_PUBLIC_KEY_SUFFIX = '.avbpubkey'
1074 PAYLOAD_PRIVATE_KEY_SUFFIX = '.pem'
1075 if not CompareKeys(
1076 payload_public_key, PAYLOAD_PUBLIC_KEY_SUFFIX,
1077 payload_private_key, PAYLOAD_PRIVATE_KEY_SUFFIX):
1078 raise ValueError("Failed to parse payload keys: \n{}".format(line))
1079
1080 container_cert = matches.group("CONTAINER_CERT")
1081 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
1082 if not CompareKeys(
1083 container_cert, OPTIONS.public_key_suffix,
1084 container_private_key, OPTIONS.private_key_suffix):
1085 raise ValueError("Failed to parse container keys: \n{}".format(line))
1086
1087 keys[name] = (payload_private_key,
1088 container_cert[:-len(OPTIONS.public_key_suffix)])
1089
1090 return keys
1091
1092
Doug Zongkereef39442009-04-02 12:14:19 -07001093def main(argv):
1094
Doug Zongker831840e2011-09-22 10:28:04 -07001095 key_mapping_options = []
1096
Doug Zongkereef39442009-04-02 12:14:19 -07001097 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001098 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001099 names, key = a.split("=")
1100 names = names.split(",")
1101 for n in names:
1102 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001103 elif o == "--extra_apex_payload_key":
1104 apex_name, key = a.split("=")
1105 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001106 elif o == "--skip_apks_with_path_prefix":
1107 # Sanity check the prefix, which must be in all upper case.
1108 prefix = a.split('/')[0]
1109 if not prefix or prefix != prefix.upper():
1110 raise ValueError("Invalid path prefix '%s'" % (a,))
1111 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001112 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001113 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001114 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001115 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001116 elif o in ("-o", "--replace_ota_keys"):
1117 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001118 elif o in ("-t", "--tag_changes"):
1119 new = []
1120 for i in a.split(","):
1121 i = i.strip()
1122 if not i or i[0] not in "-+":
1123 raise ValueError("Bad tag change '%s'" % (i,))
1124 new.append(i[0] + i[1:].strip())
1125 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001126 elif o == "--replace_verity_public_key":
1127 OPTIONS.replace_verity_public_key = (True, a)
1128 elif o == "--replace_verity_private_key":
1129 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001130 elif o == "--replace_verity_keyid":
1131 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -07001132 elif o == "--avb_vbmeta_key":
1133 OPTIONS.avb_keys['vbmeta'] = a
1134 elif o == "--avb_vbmeta_algorithm":
1135 OPTIONS.avb_algorithms['vbmeta'] = a
1136 elif o == "--avb_vbmeta_extra_args":
1137 OPTIONS.avb_extra_args['vbmeta'] = a
1138 elif o == "--avb_boot_key":
1139 OPTIONS.avb_keys['boot'] = a
1140 elif o == "--avb_boot_algorithm":
1141 OPTIONS.avb_algorithms['boot'] = a
1142 elif o == "--avb_boot_extra_args":
1143 OPTIONS.avb_extra_args['boot'] = a
1144 elif o == "--avb_dtbo_key":
1145 OPTIONS.avb_keys['dtbo'] = a
1146 elif o == "--avb_dtbo_algorithm":
1147 OPTIONS.avb_algorithms['dtbo'] = a
1148 elif o == "--avb_dtbo_extra_args":
1149 OPTIONS.avb_extra_args['dtbo'] = a
1150 elif o == "--avb_system_key":
1151 OPTIONS.avb_keys['system'] = a
1152 elif o == "--avb_system_algorithm":
1153 OPTIONS.avb_algorithms['system'] = a
1154 elif o == "--avb_system_extra_args":
1155 OPTIONS.avb_extra_args['system'] = a
1156 elif o == "--avb_vendor_key":
1157 OPTIONS.avb_keys['vendor'] = a
1158 elif o == "--avb_vendor_algorithm":
1159 OPTIONS.avb_algorithms['vendor'] = a
1160 elif o == "--avb_vendor_extra_args":
1161 OPTIONS.avb_extra_args['vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001162 elif o == "--avb_apex_extra_args":
1163 OPTIONS.avb_extra_args['apex'] = a
Doug Zongkereef39442009-04-02 12:14:19 -07001164 else:
1165 return False
1166 return True
1167
Tao Bao639118f2017-06-19 15:48:02 -07001168 args = common.ParseOptions(
1169 argv, __doc__,
1170 extra_opts="e:d:k:ot:",
1171 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001172 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001173 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001174 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001175 "default_key_mappings=",
1176 "key_mapping=",
1177 "replace_ota_keys",
1178 "tag_changes=",
1179 "replace_verity_public_key=",
1180 "replace_verity_private_key=",
1181 "replace_verity_keyid=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001182 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001183 "avb_vbmeta_algorithm=",
1184 "avb_vbmeta_key=",
1185 "avb_vbmeta_extra_args=",
1186 "avb_boot_algorithm=",
1187 "avb_boot_key=",
1188 "avb_boot_extra_args=",
1189 "avb_dtbo_algorithm=",
1190 "avb_dtbo_key=",
1191 "avb_dtbo_extra_args=",
1192 "avb_system_algorithm=",
1193 "avb_system_key=",
1194 "avb_system_extra_args=",
1195 "avb_vendor_algorithm=",
1196 "avb_vendor_key=",
1197 "avb_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -07001198 ],
1199 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001200
1201 if len(args) != 2:
1202 common.Usage(__doc__)
1203 sys.exit(1)
1204
Tao Baobadceb22019-03-15 09:33:43 -07001205 common.InitLogging()
1206
Doug Zongkereef39442009-04-02 12:14:19 -07001207 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -07001208 output_zip = zipfile.ZipFile(args[1], "w",
1209 compression=zipfile.ZIP_DEFLATED,
1210 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001211
Doug Zongker831840e2011-09-22 10:28:04 -07001212 misc_info = common.LoadInfoDict(input_zip)
1213
1214 BuildKeyMap(misc_info, key_mapping_options)
1215
Tao Baoaa7e9932019-03-15 09:37:01 -07001216 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1217 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001218
Tao Baoaa7e9932019-03-15 09:37:01 -07001219 apex_keys_info = ReadApexKeysInfo(input_zip)
1220 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1221
1222 CheckApkAndApexKeysAvailable(
1223 input_zip,
1224 set(apk_keys.keys()) | set(apex_keys.keys()),
Tao Baoe1343992019-03-19 12:24:03 -07001225 compressed_extension,
1226 apex_keys)
Tao Baoaa7e9932019-03-15 09:37:01 -07001227
1228 key_passwords = common.GetKeyPasswords(
1229 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001230 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001231 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001232
Doug Zongker412c02f2014-02-13 10:58:24 -08001233 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001234 apk_keys, apex_keys, key_passwords,
1235 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001236 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001237
Tao Bao2ed665a2015-04-01 11:21:55 -07001238 common.ZipClose(input_zip)
1239 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001240
Tianjie Xub48589a2016-08-03 19:21:52 -07001241 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001242 new_args = ["--is_signing"]
1243 # add_img_to_target_files builds the system image from scratch, so the
1244 # recovery patch is guaranteed to be regenerated there.
1245 if OPTIONS.rebuild_recovery:
1246 new_args.append("--rebuild_recovery")
1247 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001248 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001249
Tao Bao0c28d2d2017-12-24 10:37:38 -08001250 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001251
1252
1253if __name__ == '__main__':
1254 try:
1255 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001256 except common.ExternalError as e:
1257 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001258 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -07001259 finally:
1260 common.Cleanup()