blob: a9d22182b2295f8b18dcce26cd01811b4b38fe62 [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
169 We currently don't allow PRESIGNED payload / container keys.
170
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():
183 assert key, 'Presigned APEX payload for {} is not allowed'.format(apex)
184 keys_info[apex] = (key, keys_info[apex][1])
185
186 # Apply the key remapping to container keys.
187 for apex, (payload_key, container_key) in keys_info.items():
188 keys_info[apex] = (payload_key, key_map.get(container_key, container_key))
189
190 # Apply all the --extra_apks options to override the container keys.
191 for apex, key in OPTIONS.extra_apks.items():
192 # Skip non-APEX containers.
193 if apex not in keys_info:
194 continue
195 assert key, 'Presigned APEX container for {} is not allowed'.format(apex)
196 keys_info[apex][1] = key_map.get(key, key)
197
198 return keys_info
199
200
Tao Bao93c2a012018-06-19 12:19:35 -0700201def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700202 """Returns the APK info based on the given filename.
203
204 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700205 compressed extension into consideration. If it appears to be an APK file,
206 further checks if the APK file should be skipped when signing, based on the
207 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700208
209 Args:
210 filename: Path to the file.
211 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
212 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700213 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700214
215 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700216 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
217 given filename is an APK file. is_compressed indicates whether the APK file
218 is compressed (only meaningful when is_apk is True). should_be_skipped
219 indicates whether the filename matches any of the given prefixes to be
220 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700221
222 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700223 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700224 """
225 assert compressed_extension is None or compressed_extension.startswith('.'), \
226 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
227
Tao Bao93c2a012018-06-19 12:19:35 -0700228 # skipped_prefixes should be one of set/list/tuple types. Other types such as
229 # str shouldn't be accepted.
Tao Baobadceb22019-03-15 09:33:43 -0700230 assert isinstance(skipped_prefixes, (set, list, tuple)), \
231 "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
Tao Bao93c2a012018-06-19 12:19:35 -0700232
Tao Bao11f955c2018-06-19 12:19:35 -0700233 compressed_apk_extension = (
234 ".apk" + compressed_extension if compressed_extension else None)
235 is_apk = (filename.endswith(".apk") or
236 (compressed_apk_extension and
237 filename.endswith(compressed_apk_extension)))
238 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700239 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700240
241 is_compressed = (compressed_apk_extension and
242 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700243 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
244 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700245
246
Tao Baoaa7e9932019-03-15 09:37:01 -0700247def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
248 compressed_extension):
249 """Checks that all the APKs and APEXes have keys specified.
Tao Bao11f955c2018-06-19 12:19:35 -0700250
251 Args:
252 input_tf_zip: An open target_files zip file.
Tao Baoaa7e9932019-03-15 09:37:01 -0700253 known_keys: A set of APKs and APEXes that have known signing keys.
Tao Bao11f955c2018-06-19 12:19:35 -0700254 compressed_extension: The extension string of compressed APKs, such as
Tao Baoaa7e9932019-03-15 09:37:01 -0700255 '.gz', or None if there's no compressed APKs.
Tao Bao11f955c2018-06-19 12:19:35 -0700256
257 Raises:
Tao Baoaa7e9932019-03-15 09:37:01 -0700258 AssertionError: On finding unknown APKs and APEXes.
Tao Bao11f955c2018-06-19 12:19:35 -0700259 """
Tao Baoaa7e9932019-03-15 09:37:01 -0700260 unknown_files = []
Doug Zongkereb338ef2009-05-20 16:50:49 -0700261 for info in input_tf_zip.infolist():
Tao Baoaa7e9932019-03-15 09:37:01 -0700262 # Handle APEXes first, e.g. SYSTEM/apex/com.android.tzdata.apex.
263 if (info.filename.startswith('SYSTEM/apex') and
264 info.filename.endswith('.apex')):
265 name = os.path.basename(info.filename)
266 if name not in known_keys:
267 unknown_files.append(name)
268 continue
269
270 # And APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700271 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
272 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
273 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700274 continue
Tao Baoaa7e9932019-03-15 09:37:01 -0700275
Tao Bao11f955c2018-06-19 12:19:35 -0700276 name = os.path.basename(info.filename)
277 if is_compressed:
278 name = name[:-len(compressed_extension)]
Tao Baoaa7e9932019-03-15 09:37:01 -0700279 if name not in known_keys:
280 unknown_files.append(name)
Tao Bao11f955c2018-06-19 12:19:35 -0700281
Tao Baoaa7e9932019-03-15 09:37:01 -0700282 assert not unknown_files, \
Tao Bao11f955c2018-06-19 12:19:35 -0700283 ("No key specified for:\n {}\n"
284 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
Tao Baoaa7e9932019-03-15 09:37:01 -0700285 "not sign this apk).".format("\n ".join(unknown_files)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700286
287
Narayan Kamatha07bf042017-08-14 14:49:21 +0100288def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
289 is_compressed):
Doug Zongkereef39442009-04-02 12:14:19 -0700290 unsigned = tempfile.NamedTemporaryFile()
291 unsigned.write(data)
292 unsigned.flush()
293
Narayan Kamatha07bf042017-08-14 14:49:21 +0100294 if is_compressed:
295 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800296 with gzip.open(unsigned.name, "rb") as in_file, \
297 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100298 shutil.copyfileobj(in_file, out_file)
299
300 # Finally, close the "unsigned" file (which is gzip compressed), and then
301 # replace it with the uncompressed version.
302 #
303 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
304 # we could just gzip / gunzip in-memory buffers instead.
305 unsigned.close()
306 unsigned = uncompressed
307
Doug Zongkereef39442009-04-02 12:14:19 -0700308 signed = tempfile.NamedTemporaryFile()
309
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800310 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
311 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
312 # didn't change, we don't want its signature to change due to the switch
313 # from SHA-1 to SHA-256.
314 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
315 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
316 # that the APK's minSdkVersion is 1.
317 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
318 # determine whether to use SHA-256.
319 min_api_level = None
320 if platform_api_level > 23:
321 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
322 # minSdkVersion attribute
323 min_api_level = None
324 else:
325 # Force APK signer to use SHA-1
326 min_api_level = 1
327
328 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800329 min_api_level=min_api_level,
330 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700331
Tao Bao0c28d2d2017-12-24 10:37:38 -0800332 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100333 if is_compressed:
334 # Recompress the file after it has been signed.
335 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800336 with open(signed.name, "rb") as in_file, \
337 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100338 shutil.copyfileobj(in_file, out_file)
339
340 data = compressed.read()
341 compressed.close()
342 else:
343 data = signed.read()
344
Doug Zongkereef39442009-04-02 12:14:19 -0700345 unsigned.close()
346 signed.close()
347
348 return data
349
350
Tao Baoaa7e9932019-03-15 09:37:01 -0700351def SignApex(apex_data, payload_key, container_key, container_pw,
352 codename_to_api_level_map, signing_args=None):
353 """Signs the current APEX with the given payload/container keys.
354
355 Args:
356 apex_data: Raw APEX data.
357 payload_key: The path to payload signing key (w/o extension).
358 container_key: The path to container signing key (w/o extension).
359 container_pw: The matching password of the container_key, or None.
360 codename_to_api_level_map: A dict that maps from codename to API level.
361 signing_args: Additional args to be passed to the payload signer.
362
363 Returns:
364 (signed_apex, payload_key_name): signed_apex is the path to the signed APEX
365 file; payload_key_name is a str of the payload signing key name (e.g.
366 com.android.tzdata).
367 """
368 apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
369 with open(apex_file, 'wb') as apex_fp:
370 apex_fp.write(apex_data)
371
372 APEX_PAYLOAD_IMAGE = 'apex_payload.img'
373
374 # Signing an APEX is a two step process.
375 # 1. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given payload_key.
376 payload_dir = common.MakeTempDir(prefix='apex-payload-')
377 with zipfile.ZipFile(apex_file) as apex_fd:
378 payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
379
380 payload_info = apex_utils.ParseApexPayloadInfo(payload_file)
381 apex_utils.SignApexPayload(
382 payload_file,
383 payload_key,
384 payload_info['apex.key'],
385 payload_info['Algorithm'],
386 payload_info['Salt'],
387 signing_args)
388
389 common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
390 apex_zip = zipfile.ZipFile(apex_file, 'a')
391 common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
392 common.ZipClose(apex_zip)
393
394 # 2. Sign the overall APEX container with container_key.
395 signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')
396 common.SignFile(
397 apex_file,
398 signed_apex,
399 container_key,
400 container_pw,
401 codename_to_api_level_map=codename_to_api_level_map)
402
403 signed_and_aligned_apex = common.MakeTempFile(
404 prefix='apex-container-', suffix='.apex')
405 common.RunAndCheckOutput(
406 ['zipalign', '-f', '4096', signed_apex, signed_and_aligned_apex])
407
408 return (signed_and_aligned_apex, payload_info['apex.key'])
409
410
Doug Zongker412c02f2014-02-13 10:58:24 -0800411def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -0700412 apk_keys, apex_keys, key_passwords,
413 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100414 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700415 # maxsize measures the maximum filename length, including the ones to be
416 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800417 maxsize = max(
418 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700419 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700420 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800421
Tao Baoaa7e9932019-03-15 09:37:01 -0700422 # A dict of APEX payload public keys that should be updated, i.e. the files
423 # under '/system/etc/security/apex/'.
424 updated_apex_payload_keys = {}
425
Doug Zongkereef39442009-04-02 12:14:19 -0700426 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700427 filename = info.filename
428 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700429 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700430
Tao Bao33bf2682019-01-11 12:37:35 -0800431 # Skip split super images, which will be re-generated during signing.
432 if filename.startswith("OTA/") and filename.endswith(".img"):
433 continue
434
Tao Bao11f955c2018-06-19 12:19:35 -0700435 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700436 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700437 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
438 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
439
440 if is_apk and should_be_skipped:
441 # Copy skipped APKs verbatim.
442 print(
443 "NOT signing: %s\n"
444 " (skipped due to matching prefix)" % (filename,))
445 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800446
Tao Baof2cffbd2015-07-22 12:33:18 -0700447 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700448 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700449 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100450 if is_compressed:
451 name = name[:-len(compressed_extension)]
452
Tao Baoaa7e9932019-03-15 09:37:01 -0700453 key = apk_keys[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800454 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800455 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800456 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800457 codename_to_api_level_map, is_compressed)
Tao Bao2ed665a2015-04-01 11:21:55 -0700458 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700459 else:
460 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700461 print(
462 "NOT signing: %s\n"
463 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700464 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700465
Tao Baoaa7e9932019-03-15 09:37:01 -0700466 # Sign bundled APEX files.
467 elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
468 name = os.path.basename(filename)
469 payload_key, container_key = apex_keys[name]
470
471 print(" signing: %-*s container (%s)" % (maxsize, name, container_key))
472 print(" : %-*s payload (%s)" % (maxsize, name, payload_key))
473
474 (signed_apex, payload_key_name) = SignApex(
475 data,
476 payload_key,
477 container_key,
478 key_passwords[container_key],
479 codename_to_api_level_map,
480 OPTIONS.avb_extra_args.get('apex'))
481 common.ZipWrite(output_tf_zip, signed_apex, filename)
482
483 updated_apex_payload_keys[payload_key_name] = payload_key
484
485 # AVB public keys for the installed APEXes, which will be updated later.
486 elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and
487 filename != 'SYSTEM/etc/security/apex/'):
488 continue
489
Tao Baoa80ed222016-06-16 14:41:24 -0700490 # System properties.
Tao Bao11f955c2018-06-19 12:19:35 -0700491 elif filename in ("SYSTEM/build.prop",
492 "VENDOR/build.prop",
493 "SYSTEM/etc/prop.default",
494 "BOOT/RAMDISK/prop.default",
495 "BOOT/RAMDISK/default.prop", # legacy
496 "ROOT/default.prop", # legacy
497 "RECOVERY/RAMDISK/prop.default",
498 "RECOVERY/RAMDISK/default.prop"): # legacy
499 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800500 if stat.S_ISLNK(info.external_attr >> 16):
501 new_data = data
502 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800503 new_data = RewriteProps(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700504 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700505
Tao Bao66472632017-12-04 17:16:36 -0800506 # Replace the certs in *mac_permissions.xml (there could be multiple, such
507 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700508 elif filename.endswith("mac_permissions.xml"):
509 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400510 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700511 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700512
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700513 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700514 elif filename in ("SYSTEM/recovery-from-boot.p",
515 "SYSTEM/etc/recovery.img",
516 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700517 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700518
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700519 # Don't copy OTA certs if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700520 elif (
521 OPTIONS.replace_ota_keys and
522 filename in (
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700523 "BOOT/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700524 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700525 "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700526 "SYSTEM/etc/security/otacerts.zip",
527 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800528 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700529
Tao Bao46a59992017-06-05 11:55:16 -0700530 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700531 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700532 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700533
534 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700535 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700536 filename in ("BOOT/RAMDISK/verity_key",
537 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700538 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700539
Tao Bao8adcfd12016-06-17 17:01:22 -0700540 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700541 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700542 pass
543
Tianjie Xu4f099002016-08-11 18:04:27 -0700544 # Skip the care_map as we will regenerate the system/vendor images.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700545 elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700546 pass
547
Tao Baoa80ed222016-06-16 14:41:24 -0700548 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700549 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700550 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700551
Tao Baoaa7e9932019-03-15 09:37:01 -0700552 # Update APEX payload public keys.
553 for info in input_tf_zip.infolist():
554 filename = info.filename
555 if (os.path.dirname(filename) != 'SYSTEM/etc/security/apex' or
556 filename == 'SYSTEM/etc/security/apex/'):
557 continue
558
559 name = os.path.basename(filename)
560 assert name in updated_apex_payload_keys, \
561 'Unsigned APEX payload key: {}'.format(filename)
562
563 key_path = updated_apex_payload_keys[name]
564 if not os.path.exists(key_path) and not key_path.endswith('.pem'):
565 key_path = '{}.pem'.format(key_path)
566 assert os.path.exists(key_path), \
567 'Failed to find public key file {} for APEX {}'.format(
568 updated_apex_payload_keys[name], name)
569
570 print('Replacing APEX payload public key for {} with {}'.format(
571 name, key_path))
572
573 public_key = common.ExtractAvbPublicKey(key_path)
574 common.ZipWrite(output_tf_zip, public_key, arcname=filename)
575
Doug Zongker412c02f2014-02-13 10:58:24 -0800576 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700577 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800578
Tao Bao46a59992017-06-05 11:55:16 -0700579 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700580 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700581 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700582
583 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800584 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700585 # We are replacing the one in boot image only, since the one under
586 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700587 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700588 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700589
590 # Replace the keyid string in BOOT/cmdline.
591 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700592 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
593 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800594
Tao Bao639118f2017-06-19 15:48:02 -0700595 # Replace the AVB signing keys, if any.
596 ReplaceAvbSigningKeys(misc_info)
597
Tao Bao46a59992017-06-05 11:55:16 -0700598 # Write back misc_info with the latest values.
599 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
600
Doug Zongker8e931bf2009-04-06 15:21:45 -0700601
Robert Craig817c5742013-04-19 10:59:22 -0400602def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800603 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400604
Tao Bao66472632017-12-04 17:16:36 -0800605 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
606 be skipped. After the replacement, it additionally checks for duplicate
607 entries, which would otherwise fail the policy loading code in
608 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
609
610 Args:
611 data: Input string that contains a set of X.509 certs.
612
613 Returns:
614 A string after the replacement.
615
616 Raises:
617 AssertionError: On finding duplicate entries.
618 """
619 for old, new in OPTIONS.key_map.iteritems():
620 if OPTIONS.verbose:
621 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
622
623 try:
624 with open(old + ".x509.pem") as old_fp:
625 old_cert16 = base64.b16encode(
626 common.ParseCertificate(old_fp.read())).lower()
627 with open(new + ".x509.pem") as new_fp:
628 new_cert16 = base64.b16encode(
629 common.ParseCertificate(new_fp.read())).lower()
630 except IOError as e:
631 if OPTIONS.verbose or e.errno != errno.ENOENT:
632 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
633 "%s.x509.pem." % (e.filename, e.strerror, old, new))
634 continue
635
636 # Only match entire certs.
637 pattern = "\\b" + old_cert16 + "\\b"
638 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
639
640 if OPTIONS.verbose:
641 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
642 num, old, new))
643
644 # Verify that there're no duplicate entries after the replacement. Note that
645 # it's only checking entries with global seinfo at the moment (i.e. ignoring
646 # the ones with inner packages). (Bug: 69479366)
647 root = ElementTree.fromstring(data)
648 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
649 assert len(signatures) == len(set(signatures)), \
650 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400651
652 return data
653
654
Doug Zongkerc09abc82010-01-11 13:09:15 -0800655def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800656 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
657
658 Args:
659 tags: The input string that contains comma-separated tags.
660
661 Returns:
662 The updated tags (comma-separated and sorted).
663 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800664 tags = set(tags.split(","))
665 for ch in OPTIONS.tag_changes:
666 if ch[0] == "-":
667 tags.discard(ch[1:])
668 elif ch[0] == "+":
669 tags.add(ch[1:])
670 return ",".join(sorted(tags))
671
672
Tao Baoa7054ee2017-12-08 14:42:16 -0800673def RewriteProps(data):
674 """Rewrites the system properties in the given string.
675
676 Each property is expected in 'key=value' format. The properties that contain
677 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
678 EditTags().
679
680 Args:
681 data: Input string, separated by newlines.
682
683 Returns:
684 The string with modified properties.
685 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700686 output = []
687 for line in data.split("\n"):
688 line = line.strip()
689 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700690 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700691 key, value = line.split("=", 1)
Tao Baoa7054ee2017-12-08 14:42:16 -0800692 if key in ("ro.build.fingerprint", "ro.build.thumbprint",
693 "ro.vendor.build.fingerprint", "ro.vendor.build.thumbprint"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800694 pieces = value.split("/")
695 pieces[-1] = EditTags(pieces[-1])
696 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700697 elif key == "ro.bootimage.build.fingerprint":
698 pieces = value.split("/")
699 pieces[-1] = EditTags(pieces[-1])
700 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700701 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800702 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700703 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800704 pieces[-1] = EditTags(pieces[-1])
705 value = " ".join(pieces)
706 elif key == "ro.build.tags":
707 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700708 elif key == "ro.build.display.id":
709 # change, eg, "JWR66N dev-keys" to "JWR66N"
710 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700711 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800712 value.pop()
713 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800714 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700715 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800716 print(" replace: ", original_line)
717 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700718 output.append(line)
719 return "\n".join(output) + "\n"
720
721
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700722def WriteOtacerts(output_zip, filename, keys):
723 """Constructs a zipfile from given keys; and writes it to output_zip.
724
725 Args:
726 output_zip: The output target_files zip.
727 filename: The archive name in the output zip.
728 keys: A list of public keys to use during OTA package verification.
729 """
730
731 try:
732 from StringIO import StringIO
733 except ImportError:
734 from io import StringIO
735 temp_file = StringIO()
736 certs_zip = zipfile.ZipFile(temp_file, "w")
737 for k in keys:
738 common.ZipWrite(certs_zip, k)
739 common.ZipClose(certs_zip)
740 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
741
742
Doug Zongker831840e2011-09-22 10:28:04 -0700743def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700744 try:
745 keylist = input_tf_zip.read("META/otakeys.txt").split()
746 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700747 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700748
Tao Baof718f902017-11-09 10:10:10 -0800749 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800750 if extra_recovery_keys:
751 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
752 for k in extra_recovery_keys.split()]
753 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800754 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800755 else:
756 extra_recovery_keys = []
757
Doug Zongker8e931bf2009-04-06 15:21:45 -0700758 mapped_keys = []
759 for k in keylist:
760 m = re.match(r"^(.*)\.x509\.pem$", k)
761 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800762 raise common.ExternalError(
763 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700764 k = m.group(1)
765 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
766
Doug Zongkere05628c2009-08-20 17:38:42 -0700767 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800768 print("using:\n ", "\n ".join(mapped_keys))
769 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700770 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700771 devkey = misc_info.get("default_system_dev_certificate",
772 "build/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800773 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
774 if mapped_devkey != devkey:
775 misc_info["default_system_dev_certificate"] = mapped_devkey
776 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700777 print("META/otakeys.txt has no keys; using %s for OTA package"
778 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700779
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700780 # recovery now uses the same x509.pem version of the keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800781 # extra_recovery_keys are used only in recovery.
Tom Cherry2929cad2018-09-20 11:04:37 -0700782 if misc_info.get("recovery_as_boot") == "true":
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700783 recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
Tao Baoa80ed222016-06-16 14:41:24 -0700784 else:
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700785 recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
786
787 WriteOtacerts(output_tf_zip, recovery_keys_location,
788 mapped_keys + extra_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700789
790 # SystemUpdateActivity uses the x509.pem version of the keys, but
791 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800792 # We DO NOT include the extra_recovery_keys (if any) here.
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700793 WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700794
Tao Baoa80ed222016-06-16 14:41:24 -0700795 # For A/B devices, update the payload verification key.
796 if misc_info.get("ab_update") == "true":
797 # Unlike otacerts.zip that may contain multiple keys, we can only specify
798 # ONE payload verification key.
799 if len(mapped_keys) > 1:
800 print("\n WARNING: Found more than one OTA keys; Using the first one"
801 " as payload verification key.\n\n")
802
Tao Bao0c28d2d2017-12-24 10:37:38 -0800803 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800804 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700805 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700806 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700807 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
808 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700809 common.ZipWriteStr(
810 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700811 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700812 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700813
Tao Bao8adcfd12016-06-17 17:01:22 -0700814
Tao Bao0c28d2d2017-12-24 10:37:38 -0800815def ReplaceVerityPublicKey(output_zip, filename, key_path):
816 """Replaces the verity public key at the given path in the given zip.
817
818 Args:
819 output_zip: The output target_files zip.
820 filename: The archive name in the output zip.
821 key_path: The path to the public key.
822 """
823 print("Replacing verity public key with %s" % (key_path,))
824 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700825
Tao Bao8adcfd12016-06-17 17:01:22 -0700826
Tao Bao46a59992017-06-05 11:55:16 -0700827def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800828 """Replaces the verity private key in misc_info dict.
829
830 Args:
831 misc_info: The info dict.
832 key_path: The path to the private key in PKCS#8 format.
833 """
834 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700835 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700836
Tao Bao8adcfd12016-06-17 17:01:22 -0700837
Tao Baoe838d142017-12-23 23:44:48 -0800838def ReplaceVerityKeyId(input_zip, output_zip, key_path):
839 """Replaces the veritykeyid parameter in BOOT/cmdline.
840
841 Args:
842 input_zip: The input target_files zip, which should be already open.
843 output_zip: The output target_files zip, which should be already open and
844 writable.
845 key_path: The path to the PEM encoded X.509 certificate.
846 """
847 in_cmdline = input_zip.read("BOOT/cmdline")
848 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700849 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800850 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
851 return
852
Tao Bao0c28d2d2017-12-24 10:37:38 -0800853 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700854 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800855 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800856 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800857 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700858
Tao Baoe838d142017-12-23 23:44:48 -0800859 # Extract keyid using openssl command.
860 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800861 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800862 keyid, stderr = p.communicate()
863 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
864 keyid = re.search(
865 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
866 print("Replacing verity keyid with {}".format(keyid))
867 out_buffer.append("veritykeyid=id:%s" % (keyid,))
868
869 out_cmdline = ' '.join(out_buffer).strip() + '\n'
870 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700871
872
873def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
874 """Replaces META/misc_info.txt.
875
876 Only writes back the ones in the original META/misc_info.txt. Because the
877 current in-memory dict contains additional items computed at runtime.
878 """
879 misc_info_old = common.LoadDictionaryFromLines(
880 input_zip.read('META/misc_info.txt').split('\n'))
881 items = []
882 for key in sorted(misc_info):
883 if key in misc_info_old:
884 items.append('%s=%s' % (key, misc_info[key]))
885 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700886
Tao Bao8adcfd12016-06-17 17:01:22 -0700887
Tao Bao639118f2017-06-19 15:48:02 -0700888def ReplaceAvbSigningKeys(misc_info):
889 """Replaces the AVB signing keys."""
890
891 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800892 'boot' : 'avb_boot_add_hash_footer_args',
893 'dtbo' : 'avb_dtbo_add_hash_footer_args',
894 'recovery' : 'avb_recovery_add_hash_footer_args',
895 'system' : 'avb_system_add_hashtree_footer_args',
896 'vendor' : 'avb_vendor_add_hashtree_footer_args',
897 'vbmeta' : 'avb_vbmeta_args',
Tao Bao639118f2017-06-19 15:48:02 -0700898 }
899
900 def ReplaceAvbPartitionSigningKey(partition):
901 key = OPTIONS.avb_keys.get(partition)
902 if not key:
903 return
904
905 algorithm = OPTIONS.avb_algorithms.get(partition)
906 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
907
Tao Bao0c28d2d2017-12-24 10:37:38 -0800908 print('Replacing AVB signing key for %s with "%s" (%s)' % (
909 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700910 misc_info['avb_' + partition + '_algorithm'] = algorithm
911 misc_info['avb_' + partition + '_key_path'] = key
912
913 extra_args = OPTIONS.avb_extra_args.get(partition)
914 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800915 print('Setting extra AVB signing args for %s to "%s"' % (
916 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700917 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
918 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
919
920 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
921 ReplaceAvbPartitionSigningKey(partition)
922
923
Doug Zongker831840e2011-09-22 10:28:04 -0700924def BuildKeyMap(misc_info, key_mapping_options):
925 for s, d in key_mapping_options:
926 if s is None: # -d option
927 devkey = misc_info.get("default_system_dev_certificate",
928 "build/target/product/security/testkey")
929 devkeydir = os.path.dirname(devkey)
930
931 OPTIONS.key_map.update({
932 devkeydir + "/testkey": d + "/releasekey",
933 devkeydir + "/devkey": d + "/releasekey",
934 devkeydir + "/media": d + "/media",
935 devkeydir + "/shared": d + "/shared",
936 devkeydir + "/platform": d + "/platform",
937 })
938 else:
939 OPTIONS.key_map[s] = d
940
941
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800942def GetApiLevelAndCodename(input_tf_zip):
943 data = input_tf_zip.read("SYSTEM/build.prop")
944 api_level = None
945 codename = None
946 for line in data.split("\n"):
947 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800948 if line and line[0] != '#' and "=" in line:
949 key, value = line.split("=", 1)
950 key = key.strip()
951 if key == "ro.build.version.sdk":
952 api_level = int(value.strip())
953 elif key == "ro.build.version.codename":
954 codename = value.strip()
955
956 if api_level is None:
957 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
958 if codename is None:
959 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
960
961 return (api_level, codename)
962
963
964def GetCodenameToApiLevelMap(input_tf_zip):
965 data = input_tf_zip.read("SYSTEM/build.prop")
966 api_level = None
967 codenames = None
968 for line in data.split("\n"):
969 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800970 if line and line[0] != '#' and "=" in line:
971 key, value = line.split("=", 1)
972 key = key.strip()
973 if key == "ro.build.version.sdk":
974 api_level = int(value.strip())
975 elif key == "ro.build.version.all_codenames":
976 codenames = value.strip().split(",")
977
978 if api_level is None:
979 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
980 if codenames is None:
981 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
982
983 result = dict()
984 for codename in codenames:
985 codename = codename.strip()
Tao Baobadceb22019-03-15 09:33:43 -0700986 if codename:
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800987 result[codename] = api_level
988 return result
989
990
Tao Baoaa7e9932019-03-15 09:37:01 -0700991def ReadApexKeysInfo(tf_zip):
992 """Parses the APEX keys info from a given target-files zip.
993
994 Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
995 dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
996 tuple of (payload_key, container_key).
997
998 Args:
999 tf_zip: The input target_files ZipFile (already open).
1000
1001 Returns:
1002 (payload_key, container_key): payload_key contains the path to the payload
1003 signing key; container_key contains the path to the container signing
1004 key.
1005 """
1006 keys = {}
1007 for line in tf_zip.read("META/apexkeys.txt").split("\n"):
1008 line = line.strip()
1009 if not line:
1010 continue
1011 matches = re.match(
1012 r'^name="(?P<NAME>.*)"\s+'
1013 r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1014 r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1015 r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
1016 r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*)"$',
1017 line)
1018 if not matches:
1019 continue
1020
1021 name = matches.group('NAME')
1022 payload_public_key = matches.group("PAYLOAD_PUBLIC_KEY")
1023 payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1024
1025 def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1026 pubkey_suffix_len = len(pubkey_suffix)
1027 privkey_suffix_len = len(privkey_suffix)
1028 return (pubkey.endswith(pubkey_suffix) and
1029 privkey.endswith(privkey_suffix) and
1030 pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1031
1032 PAYLOAD_PUBLIC_KEY_SUFFIX = '.avbpubkey'
1033 PAYLOAD_PRIVATE_KEY_SUFFIX = '.pem'
1034 if not CompareKeys(
1035 payload_public_key, PAYLOAD_PUBLIC_KEY_SUFFIX,
1036 payload_private_key, PAYLOAD_PRIVATE_KEY_SUFFIX):
1037 raise ValueError("Failed to parse payload keys: \n{}".format(line))
1038
1039 container_cert = matches.group("CONTAINER_CERT")
1040 container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
1041 if not CompareKeys(
1042 container_cert, OPTIONS.public_key_suffix,
1043 container_private_key, OPTIONS.private_key_suffix):
1044 raise ValueError("Failed to parse container keys: \n{}".format(line))
1045
1046 keys[name] = (payload_private_key,
1047 container_cert[:-len(OPTIONS.public_key_suffix)])
1048
1049 return keys
1050
1051
Doug Zongkereef39442009-04-02 12:14:19 -07001052def main(argv):
1053
Doug Zongker831840e2011-09-22 10:28:04 -07001054 key_mapping_options = []
1055
Doug Zongkereef39442009-04-02 12:14:19 -07001056 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -07001057 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -07001058 names, key = a.split("=")
1059 names = names.split(",")
1060 for n in names:
1061 OPTIONS.extra_apks[n] = key
Tao Baoaa7e9932019-03-15 09:37:01 -07001062 elif o == "--extra_apex_payload_key":
1063 apex_name, key = a.split("=")
1064 OPTIONS.extra_apex_payload_keys[apex_name] = key
Tao Bao93c2a012018-06-19 12:19:35 -07001065 elif o == "--skip_apks_with_path_prefix":
1066 # Sanity check the prefix, which must be in all upper case.
1067 prefix = a.split('/')[0]
1068 if not prefix or prefix != prefix.upper():
1069 raise ValueError("Invalid path prefix '%s'" % (a,))
1070 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001071 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -07001072 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -07001073 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -07001074 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -07001075 elif o in ("-o", "--replace_ota_keys"):
1076 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -07001077 elif o in ("-t", "--tag_changes"):
1078 new = []
1079 for i in a.split(","):
1080 i = i.strip()
1081 if not i or i[0] not in "-+":
1082 raise ValueError("Bad tag change '%s'" % (i,))
1083 new.append(i[0] + i[1:].strip())
1084 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -07001085 elif o == "--replace_verity_public_key":
1086 OPTIONS.replace_verity_public_key = (True, a)
1087 elif o == "--replace_verity_private_key":
1088 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -07001089 elif o == "--replace_verity_keyid":
1090 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -07001091 elif o == "--avb_vbmeta_key":
1092 OPTIONS.avb_keys['vbmeta'] = a
1093 elif o == "--avb_vbmeta_algorithm":
1094 OPTIONS.avb_algorithms['vbmeta'] = a
1095 elif o == "--avb_vbmeta_extra_args":
1096 OPTIONS.avb_extra_args['vbmeta'] = a
1097 elif o == "--avb_boot_key":
1098 OPTIONS.avb_keys['boot'] = a
1099 elif o == "--avb_boot_algorithm":
1100 OPTIONS.avb_algorithms['boot'] = a
1101 elif o == "--avb_boot_extra_args":
1102 OPTIONS.avb_extra_args['boot'] = a
1103 elif o == "--avb_dtbo_key":
1104 OPTIONS.avb_keys['dtbo'] = a
1105 elif o == "--avb_dtbo_algorithm":
1106 OPTIONS.avb_algorithms['dtbo'] = a
1107 elif o == "--avb_dtbo_extra_args":
1108 OPTIONS.avb_extra_args['dtbo'] = a
1109 elif o == "--avb_system_key":
1110 OPTIONS.avb_keys['system'] = a
1111 elif o == "--avb_system_algorithm":
1112 OPTIONS.avb_algorithms['system'] = a
1113 elif o == "--avb_system_extra_args":
1114 OPTIONS.avb_extra_args['system'] = a
1115 elif o == "--avb_vendor_key":
1116 OPTIONS.avb_keys['vendor'] = a
1117 elif o == "--avb_vendor_algorithm":
1118 OPTIONS.avb_algorithms['vendor'] = a
1119 elif o == "--avb_vendor_extra_args":
1120 OPTIONS.avb_extra_args['vendor'] = a
Tao Baoaa7e9932019-03-15 09:37:01 -07001121 elif o == "--avb_apex_extra_args":
1122 OPTIONS.avb_extra_args['apex'] = a
Doug Zongkereef39442009-04-02 12:14:19 -07001123 else:
1124 return False
1125 return True
1126
Tao Bao639118f2017-06-19 15:48:02 -07001127 args = common.ParseOptions(
1128 argv, __doc__,
1129 extra_opts="e:d:k:ot:",
1130 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -08001131 "extra_apks=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001132 "extra_apex_payload_key=",
Tao Bao93c2a012018-06-19 12:19:35 -07001133 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001134 "default_key_mappings=",
1135 "key_mapping=",
1136 "replace_ota_keys",
1137 "tag_changes=",
1138 "replace_verity_public_key=",
1139 "replace_verity_private_key=",
1140 "replace_verity_keyid=",
Tao Baoaa7e9932019-03-15 09:37:01 -07001141 "avb_apex_extra_args=",
Tao Bao0c28d2d2017-12-24 10:37:38 -08001142 "avb_vbmeta_algorithm=",
1143 "avb_vbmeta_key=",
1144 "avb_vbmeta_extra_args=",
1145 "avb_boot_algorithm=",
1146 "avb_boot_key=",
1147 "avb_boot_extra_args=",
1148 "avb_dtbo_algorithm=",
1149 "avb_dtbo_key=",
1150 "avb_dtbo_extra_args=",
1151 "avb_system_algorithm=",
1152 "avb_system_key=",
1153 "avb_system_extra_args=",
1154 "avb_vendor_algorithm=",
1155 "avb_vendor_key=",
1156 "avb_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -07001157 ],
1158 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001159
1160 if len(args) != 2:
1161 common.Usage(__doc__)
1162 sys.exit(1)
1163
Tao Baobadceb22019-03-15 09:33:43 -07001164 common.InitLogging()
1165
Doug Zongkereef39442009-04-02 12:14:19 -07001166 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -07001167 output_zip = zipfile.ZipFile(args[1], "w",
1168 compression=zipfile.ZIP_DEFLATED,
1169 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -07001170
Doug Zongker831840e2011-09-22 10:28:04 -07001171 misc_info = common.LoadInfoDict(input_zip)
1172
1173 BuildKeyMap(misc_info, key_mapping_options)
1174
Tao Baoaa7e9932019-03-15 09:37:01 -07001175 apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1176 apk_keys = GetApkCerts(apk_keys_info)
Doug Zongkereb338ef2009-05-20 16:50:49 -07001177
Tao Baoaa7e9932019-03-15 09:37:01 -07001178 apex_keys_info = ReadApexKeysInfo(input_zip)
1179 apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1180
1181 CheckApkAndApexKeysAvailable(
1182 input_zip,
1183 set(apk_keys.keys()) | set(apex_keys.keys()),
1184 compressed_extension)
1185
1186 key_passwords = common.GetKeyPasswords(
1187 set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
Tao Bao9aa4b9b2016-09-29 17:53:56 -07001188 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001189 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -08001190
Doug Zongker412c02f2014-02-13 10:58:24 -08001191 ProcessTargetFiles(input_zip, output_zip, misc_info,
Tao Baoaa7e9932019-03-15 09:37:01 -07001192 apk_keys, apex_keys, key_passwords,
1193 platform_api_level, codename_to_api_level_map,
Narayan Kamatha07bf042017-08-14 14:49:21 +01001194 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -07001195
Tao Bao2ed665a2015-04-01 11:21:55 -07001196 common.ZipClose(input_zip)
1197 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001198
Tianjie Xub48589a2016-08-03 19:21:52 -07001199 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -07001200 new_args = ["--is_signing"]
1201 # add_img_to_target_files builds the system image from scratch, so the
1202 # recovery patch is guaranteed to be regenerated there.
1203 if OPTIONS.rebuild_recovery:
1204 new_args.append("--rebuild_recovery")
1205 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -07001206 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -07001207
Tao Bao0c28d2d2017-12-24 10:37:38 -08001208 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001209
1210
1211if __name__ == '__main__':
1212 try:
1213 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -08001214 except common.ExternalError as e:
1215 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001216 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -07001217 finally:
1218 common.Cleanup()