blob: 5c6c8e1fce10b11b8ad953df39f94cd70a8ee58c [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>
24 Add extra APK name/key pairs as though they appeared in
Doug Zongkerad88c7c2009-04-14 12:34:27 -070025 apkcerts.txt (so mappings specified by -k and -d are applied).
26 Keys specified in -e override any value for that app contained
27 in the apkcerts.txt file. Option may be repeated to give
28 multiple extra packages.
Doug Zongkereef39442009-04-02 12:14:19 -070029
Tao Bao93c2a012018-06-19 12:19:35 -070030 --skip_apks_with_path_prefix <prefix>
31 Skip signing an APK if it has the matching prefix in its path. The prefix
32 should be matching the entry name, which has partition names in upper
33 case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be
34 repeated to give multiple prefixes.
35
Doug Zongkereef39442009-04-02 12:14:19 -070036 -k (--key_mapping) <src_key=dest_key>
37 Add a mapping from the key name as specified in apkcerts.txt (the
38 src_key) to the real key you wish to sign the package with
39 (dest_key). Option may be repeated to give multiple key
40 mappings.
41
42 -d (--default_key_mappings) <dir>
43 Set up the following key mappings:
44
Doug Zongker831840e2011-09-22 10:28:04 -070045 $devkey/devkey ==> $dir/releasekey
46 $devkey/testkey ==> $dir/releasekey
47 $devkey/media ==> $dir/media
48 $devkey/shared ==> $dir/shared
49 $devkey/platform ==> $dir/platform
50
51 where $devkey is the directory part of the value of
52 default_system_dev_certificate from the input target-files's
53 META/misc_info.txt. (Defaulting to "build/target/product/security"
54 if the value is not present in misc_info.
Doug Zongkereef39442009-04-02 12:14:19 -070055
56 -d and -k options are added to the set of mappings in the order
57 in which they appear on the command line.
Doug Zongker8e931bf2009-04-06 15:21:45 -070058
59 -o (--replace_ota_keys)
Tao Baoa80ed222016-06-16 14:41:24 -070060 Replace the certificate (public key) used by OTA package verification
61 with the ones specified in the input target_files zip (in the
62 META/otakeys.txt file). Key remapping (-k and -d) is performed on the
63 keys. For A/B devices, the payload verification key will be replaced
64 as well. If there're multiple OTA keys, only the first one will be used
65 for payload verification.
Doug Zongker17aa9442009-04-17 10:15:58 -070066
Doug Zongkerae877012009-04-21 10:04:51 -070067 -t (--tag_changes) <+tag>,<-tag>,...
68 Comma-separated list of changes to make to the set of tags (in
69 the last component of the build fingerprint). Prefix each with
70 '+' or '-' to indicate whether that tag should be added or
71 removed. Changes are processed in the order they appear.
Doug Zongker831840e2011-09-22 10:28:04 -070072 Default value is "-test-keys,-dev-keys,+release-keys".
Doug Zongkerae877012009-04-21 10:04:51 -070073
Tao Bao8adcfd12016-06-17 17:01:22 -070074 --replace_verity_private_key <key>
75 Replace the private key used for verity signing. It expects a filename
76 WITHOUT the extension (e.g. verity_key).
77
78 --replace_verity_public_key <key>
79 Replace the certificate (public key) used for verity verification. The
80 key file replaces the one at BOOT/RAMDISK/verity_key (or ROOT/verity_key
81 for devices using system_root_image). It expects the key filename WITH
82 the extension (e.g. verity_key.pub).
83
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -070084 --replace_verity_keyid <path_to_X509_PEM_cert_file>
85 Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
Tao Bao8adcfd12016-06-17 17:01:22 -070086 with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
Tao Bao639118f2017-06-19 15:48:02 -070087
88 --avb_{boot,system,vendor,dtbo,vbmeta}_algorithm <algorithm>
89 --avb_{boot,system,vendor,dtbo,vbmeta}_key <key>
90 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
91 the specified image. Otherwise it uses the existing values in info dict.
92
93 --avb_{boot,system,vendor,dtbo,vbmeta}_extra_args <args>
94 Specify any additional args that are needed to AVB-sign the image
95 (e.g. "--signing_helper /path/to/helper"). The args will be appended to
96 the existing ones in info dict.
Doug Zongkereef39442009-04-02 12:14:19 -070097"""
98
Tao Bao0c28d2d2017-12-24 10:37:38 -080099from __future__ import print_function
Doug Zongkereef39442009-04-02 12:14:19 -0700100
Robert Craig817c5742013-04-19 10:59:22 -0400101import base64
Doug Zongker8e931bf2009-04-06 15:21:45 -0700102import copy
Robert Craig817c5742013-04-19 10:59:22 -0400103import errno
Narayan Kamatha07bf042017-08-14 14:49:21 +0100104import gzip
Doug Zongkereef39442009-04-02 12:14:19 -0700105import os
106import re
Narayan Kamatha07bf042017-08-14 14:49:21 +0100107import shutil
Tao Bao9fdd00f2017-07-12 11:57:05 -0700108import stat
Doug Zongkereef39442009-04-02 12:14:19 -0700109import subprocess
Tao Bao0c28d2d2017-12-24 10:37:38 -0800110import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700111import tempfile
112import zipfile
Tao Bao66472632017-12-04 17:16:36 -0800113from xml.etree import ElementTree
Doug Zongkereef39442009-04-02 12:14:19 -0700114
Doug Zongker3c84f562014-07-31 11:06:30 -0700115import add_img_to_target_files
Doug Zongkereef39442009-04-02 12:14:19 -0700116import common
117
Tao Bao0c28d2d2017-12-24 10:37:38 -0800118
119if sys.hexversion < 0x02070000:
120 print("Python 2.7 or newer is required.", file=sys.stderr)
121 sys.exit(1)
122
123
Doug Zongkereef39442009-04-02 12:14:19 -0700124OPTIONS = common.OPTIONS
125
126OPTIONS.extra_apks = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700127OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700128OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700129OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700130OPTIONS.replace_ota_keys = False
Geremy Condraf19b3652014-07-29 17:54:54 -0700131OPTIONS.replace_verity_public_key = False
132OPTIONS.replace_verity_private_key = False
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700133OPTIONS.replace_verity_keyid = False
Doug Zongker831840e2011-09-22 10:28:04 -0700134OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700135OPTIONS.avb_keys = {}
136OPTIONS.avb_algorithms = {}
137OPTIONS.avb_extra_args = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700138
Tao Bao0c28d2d2017-12-24 10:37:38 -0800139
Narayan Kamatha07bf042017-08-14 14:49:21 +0100140def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800141 # apply the key remapping to the contents of the file
142 for apk, cert in certmap.iteritems():
143 certmap[apk] = OPTIONS.key_map.get(cert, cert)
144
145 # apply all the -e options, overriding anything in the file
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700146 for apk, cert in OPTIONS.extra_apks.iteritems():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800147 if not cert:
148 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700149 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800150
Doug Zongkereef39442009-04-02 12:14:19 -0700151 return certmap
152
153
Tao Bao93c2a012018-06-19 12:19:35 -0700154def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700155 """Returns the APK info based on the given filename.
156
157 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700158 compressed extension into consideration. If it appears to be an APK file,
159 further checks if the APK file should be skipped when signing, based on the
160 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700161
162 Args:
163 filename: Path to the file.
164 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
165 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700166 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700167
168 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700169 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
170 given filename is an APK file. is_compressed indicates whether the APK file
171 is compressed (only meaningful when is_apk is True). should_be_skipped
172 indicates whether the filename matches any of the given prefixes to be
173 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700174
175 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700176 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700177 """
178 assert compressed_extension is None or compressed_extension.startswith('.'), \
179 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
180
Tao Bao93c2a012018-06-19 12:19:35 -0700181 # skipped_prefixes should be one of set/list/tuple types. Other types such as
182 # str shouldn't be accepted.
183 assert (isinstance(skipped_prefixes, tuple) or
184 isinstance(skipped_prefixes, set) or
185 isinstance(skipped_prefixes, list)), \
186 "Invalid skipped_prefixes input type: {}".format(
187 type(skipped_prefixes))
188
Tao Bao11f955c2018-06-19 12:19:35 -0700189 compressed_apk_extension = (
190 ".apk" + compressed_extension if compressed_extension else None)
191 is_apk = (filename.endswith(".apk") or
192 (compressed_apk_extension and
193 filename.endswith(compressed_apk_extension)))
194 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700195 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700196
197 is_compressed = (compressed_apk_extension and
198 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700199 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
200 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700201
202
Narayan Kamatha07bf042017-08-14 14:49:21 +0100203def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension):
Tao Bao11f955c2018-06-19 12:19:35 -0700204 """Checks that all the APKs have keys specified, otherwise errors out.
205
206 Args:
207 input_tf_zip: An open target_files zip file.
208 apk_key_map: A dict of known signing keys key'd by APK names.
209 compressed_extension: The extension string of compressed APKs, such as
210 ".gz", or None if there's no compressed APKs.
211
212 Raises:
213 AssertionError: On finding unknown APKs.
214 """
Doug Zongkereb338ef2009-05-20 16:50:49 -0700215 unknown_apks = []
216 for info in input_tf_zip.infolist():
Tao Bao93c2a012018-06-19 12:19:35 -0700217 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
218 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
219 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700220 continue
221 name = os.path.basename(info.filename)
222 if is_compressed:
223 name = name[:-len(compressed_extension)]
224 if name not in apk_key_map:
225 unknown_apks.append(name)
226
227 assert not unknown_apks, \
228 ("No key specified for:\n {}\n"
229 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
230 "not sign this apk).".format("\n ".join(unknown_apks)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700231
232
Narayan Kamatha07bf042017-08-14 14:49:21 +0100233def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
234 is_compressed):
Doug Zongkereef39442009-04-02 12:14:19 -0700235 unsigned = tempfile.NamedTemporaryFile()
236 unsigned.write(data)
237 unsigned.flush()
238
Narayan Kamatha07bf042017-08-14 14:49:21 +0100239 if is_compressed:
240 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800241 with gzip.open(unsigned.name, "rb") as in_file, \
242 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100243 shutil.copyfileobj(in_file, out_file)
244
245 # Finally, close the "unsigned" file (which is gzip compressed), and then
246 # replace it with the uncompressed version.
247 #
248 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
249 # we could just gzip / gunzip in-memory buffers instead.
250 unsigned.close()
251 unsigned = uncompressed
252
Doug Zongkereef39442009-04-02 12:14:19 -0700253 signed = tempfile.NamedTemporaryFile()
254
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800255 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
256 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
257 # didn't change, we don't want its signature to change due to the switch
258 # from SHA-1 to SHA-256.
259 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
260 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
261 # that the APK's minSdkVersion is 1.
262 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
263 # determine whether to use SHA-256.
264 min_api_level = None
265 if platform_api_level > 23:
266 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
267 # minSdkVersion attribute
268 min_api_level = None
269 else:
270 # Force APK signer to use SHA-1
271 min_api_level = 1
272
273 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800274 min_api_level=min_api_level,
275 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700276
Tao Bao0c28d2d2017-12-24 10:37:38 -0800277 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100278 if is_compressed:
279 # Recompress the file after it has been signed.
280 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800281 with open(signed.name, "rb") as in_file, \
282 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100283 shutil.copyfileobj(in_file, out_file)
284
285 data = compressed.read()
286 compressed.close()
287 else:
288 data = signed.read()
289
Doug Zongkereef39442009-04-02 12:14:19 -0700290 unsigned.close()
291 signed.close()
292
293 return data
294
295
Doug Zongker412c02f2014-02-13 10:58:24 -0800296def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800297 apk_key_map, key_passwords, platform_api_level,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100298 codename_to_api_level_map,
299 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700300 # maxsize measures the maximum filename length, including the ones to be
301 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800302 maxsize = max(
303 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700304 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700305 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800306
Doug Zongkereef39442009-04-02 12:14:19 -0700307 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700308 filename = info.filename
309 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700310 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700311
Tao Bao11f955c2018-06-19 12:19:35 -0700312 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700313 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700314 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
315 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
316
317 if is_apk and should_be_skipped:
318 # Copy skipped APKs verbatim.
319 print(
320 "NOT signing: %s\n"
321 " (skipped due to matching prefix)" % (filename,))
322 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800323
Tao Baof2cffbd2015-07-22 12:33:18 -0700324 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700325 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700326 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100327 if is_compressed:
328 name = name[:-len(compressed_extension)]
329
Doug Zongker43874f82009-04-14 14:05:15 -0700330 key = apk_key_map[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800331 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800332 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800333 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800334 codename_to_api_level_map, is_compressed)
Tao Bao2ed665a2015-04-01 11:21:55 -0700335 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700336 else:
337 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700338 print(
339 "NOT signing: %s\n"
340 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700341 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700342
343 # System properties.
Tao Bao11f955c2018-06-19 12:19:35 -0700344 elif filename in ("SYSTEM/build.prop",
345 "VENDOR/build.prop",
346 "SYSTEM/etc/prop.default",
347 "BOOT/RAMDISK/prop.default",
348 "BOOT/RAMDISK/default.prop", # legacy
349 "ROOT/default.prop", # legacy
350 "RECOVERY/RAMDISK/prop.default",
351 "RECOVERY/RAMDISK/default.prop"): # legacy
352 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800353 if stat.S_ISLNK(info.external_attr >> 16):
354 new_data = data
355 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800356 new_data = RewriteProps(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700357 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700358
Tao Bao66472632017-12-04 17:16:36 -0800359 # Replace the certs in *mac_permissions.xml (there could be multiple, such
360 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700361 elif filename.endswith("mac_permissions.xml"):
362 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400363 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700364 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700365
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700366 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700367 elif filename in ("SYSTEM/recovery-from-boot.p",
368 "SYSTEM/etc/recovery.img",
369 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700370 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700371
372 # Don't copy OTA keys if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700373 elif (
374 OPTIONS.replace_ota_keys and
375 filename in (
376 "BOOT/RAMDISK/res/keys",
377 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
378 "RECOVERY/RAMDISK/res/keys",
379 "SYSTEM/etc/security/otacerts.zip",
380 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800381 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700382
Tao Bao46a59992017-06-05 11:55:16 -0700383 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700384 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700385 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700386
387 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700388 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700389 filename in ("BOOT/RAMDISK/verity_key",
390 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700391 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700392
Tao Bao8adcfd12016-06-17 17:01:22 -0700393 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700394 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700395 pass
396
Tianjie Xu4f099002016-08-11 18:04:27 -0700397 # Skip the care_map as we will regenerate the system/vendor images.
Tao Bao11f955c2018-06-19 12:19:35 -0700398 elif filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700399 pass
400
Tao Baoa80ed222016-06-16 14:41:24 -0700401 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700402 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700403 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700404
Doug Zongker412c02f2014-02-13 10:58:24 -0800405 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700406 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800407
Tao Bao46a59992017-06-05 11:55:16 -0700408 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700409 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700410 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700411
412 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800413 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700414 # We are replacing the one in boot image only, since the one under
415 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700416 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700417 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700418
419 # Replace the keyid string in BOOT/cmdline.
420 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700421 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
422 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800423
Tao Bao639118f2017-06-19 15:48:02 -0700424 # Replace the AVB signing keys, if any.
425 ReplaceAvbSigningKeys(misc_info)
426
Tao Bao46a59992017-06-05 11:55:16 -0700427 # Write back misc_info with the latest values.
428 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
429
Doug Zongker8e931bf2009-04-06 15:21:45 -0700430
Robert Craig817c5742013-04-19 10:59:22 -0400431def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800432 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400433
Tao Bao66472632017-12-04 17:16:36 -0800434 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
435 be skipped. After the replacement, it additionally checks for duplicate
436 entries, which would otherwise fail the policy loading code in
437 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
438
439 Args:
440 data: Input string that contains a set of X.509 certs.
441
442 Returns:
443 A string after the replacement.
444
445 Raises:
446 AssertionError: On finding duplicate entries.
447 """
448 for old, new in OPTIONS.key_map.iteritems():
449 if OPTIONS.verbose:
450 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
451
452 try:
453 with open(old + ".x509.pem") as old_fp:
454 old_cert16 = base64.b16encode(
455 common.ParseCertificate(old_fp.read())).lower()
456 with open(new + ".x509.pem") as new_fp:
457 new_cert16 = base64.b16encode(
458 common.ParseCertificate(new_fp.read())).lower()
459 except IOError as e:
460 if OPTIONS.verbose or e.errno != errno.ENOENT:
461 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
462 "%s.x509.pem." % (e.filename, e.strerror, old, new))
463 continue
464
465 # Only match entire certs.
466 pattern = "\\b" + old_cert16 + "\\b"
467 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
468
469 if OPTIONS.verbose:
470 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
471 num, old, new))
472
473 # Verify that there're no duplicate entries after the replacement. Note that
474 # it's only checking entries with global seinfo at the moment (i.e. ignoring
475 # the ones with inner packages). (Bug: 69479366)
476 root = ElementTree.fromstring(data)
477 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
478 assert len(signatures) == len(set(signatures)), \
479 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400480
481 return data
482
483
Doug Zongkerc09abc82010-01-11 13:09:15 -0800484def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800485 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
486
487 Args:
488 tags: The input string that contains comma-separated tags.
489
490 Returns:
491 The updated tags (comma-separated and sorted).
492 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800493 tags = set(tags.split(","))
494 for ch in OPTIONS.tag_changes:
495 if ch[0] == "-":
496 tags.discard(ch[1:])
497 elif ch[0] == "+":
498 tags.add(ch[1:])
499 return ",".join(sorted(tags))
500
501
Tao Baoa7054ee2017-12-08 14:42:16 -0800502def RewriteProps(data):
503 """Rewrites the system properties in the given string.
504
505 Each property is expected in 'key=value' format. The properties that contain
506 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
507 EditTags().
508
509 Args:
510 data: Input string, separated by newlines.
511
512 Returns:
513 The string with modified properties.
514 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700515 output = []
516 for line in data.split("\n"):
517 line = line.strip()
518 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700519 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700520 key, value = line.split("=", 1)
Tao Baoa7054ee2017-12-08 14:42:16 -0800521 if key in ("ro.build.fingerprint", "ro.build.thumbprint",
522 "ro.vendor.build.fingerprint", "ro.vendor.build.thumbprint"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800523 pieces = value.split("/")
524 pieces[-1] = EditTags(pieces[-1])
525 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700526 elif key == "ro.bootimage.build.fingerprint":
527 pieces = value.split("/")
528 pieces[-1] = EditTags(pieces[-1])
529 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700530 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800531 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700532 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800533 pieces[-1] = EditTags(pieces[-1])
534 value = " ".join(pieces)
535 elif key == "ro.build.tags":
536 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700537 elif key == "ro.build.display.id":
538 # change, eg, "JWR66N dev-keys" to "JWR66N"
539 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700540 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800541 value.pop()
542 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800543 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700544 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800545 print(" replace: ", original_line)
546 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700547 output.append(line)
548 return "\n".join(output) + "\n"
549
550
Doug Zongker831840e2011-09-22 10:28:04 -0700551def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700552 try:
553 keylist = input_tf_zip.read("META/otakeys.txt").split()
554 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700555 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700556
Tao Baof718f902017-11-09 10:10:10 -0800557 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800558 if extra_recovery_keys:
559 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
560 for k in extra_recovery_keys.split()]
561 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800562 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800563 else:
564 extra_recovery_keys = []
565
Doug Zongker8e931bf2009-04-06 15:21:45 -0700566 mapped_keys = []
567 for k in keylist:
568 m = re.match(r"^(.*)\.x509\.pem$", k)
569 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800570 raise common.ExternalError(
571 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700572 k = m.group(1)
573 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
574
Doug Zongkere05628c2009-08-20 17:38:42 -0700575 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800576 print("using:\n ", "\n ".join(mapped_keys))
577 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700578 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700579 devkey = misc_info.get("default_system_dev_certificate",
580 "build/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800581 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
582 if mapped_devkey != devkey:
583 misc_info["default_system_dev_certificate"] = mapped_devkey
584 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700585 print("META/otakeys.txt has no keys; using %s for OTA package"
586 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700587
588 # recovery uses a version of the key that has been slightly
589 # predigested (by DumpPublicKey.java) and put in res/keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800590 # extra_recovery_keys are used only in recovery.
Tao Baoe95540e2016-11-08 12:08:53 -0800591 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
592 ["-jar",
593 os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")] +
594 mapped_keys + extra_recovery_keys)
595 p = common.Run(cmd, stdout=subprocess.PIPE)
Doug Zongker412c02f2014-02-13 10:58:24 -0800596 new_recovery_keys, _ = p.communicate()
Doug Zongker8e931bf2009-04-06 15:21:45 -0700597 if p.returncode != 0:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700598 raise common.ExternalError("failed to run dumpkeys")
Tao Baoa80ed222016-06-16 14:41:24 -0700599
600 # system_root_image puts the recovery keys at BOOT/RAMDISK.
601 if misc_info.get("system_root_image") == "true":
602 recovery_keys_location = "BOOT/RAMDISK/res/keys"
603 else:
604 recovery_keys_location = "RECOVERY/RAMDISK/res/keys"
605 common.ZipWriteStr(output_tf_zip, recovery_keys_location, new_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700606
607 # SystemUpdateActivity uses the x509.pem version of the keys, but
608 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800609 # We DO NOT include the extra_recovery_keys (if any) here.
Doug Zongker8e931bf2009-04-06 15:21:45 -0700610
Tao Bao0c28d2d2017-12-24 10:37:38 -0800611 try:
612 from StringIO import StringIO
613 except ImportError:
614 from io import StringIO
615 temp_file = StringIO()
Dan Albert8b72aef2015-03-23 19:13:21 -0700616 certs_zip = zipfile.ZipFile(temp_file, "w")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700617 for k in mapped_keys:
Tao Bao83cd79d2016-04-11 23:05:52 -0700618 common.ZipWrite(certs_zip, k)
619 common.ZipClose(certs_zip)
Doug Zongker048e7ca2009-06-15 14:31:53 -0700620 common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
Dan Albert8b72aef2015-03-23 19:13:21 -0700621 temp_file.getvalue())
Doug Zongkereef39442009-04-02 12:14:19 -0700622
Tao Baoa80ed222016-06-16 14:41:24 -0700623 # For A/B devices, update the payload verification key.
624 if misc_info.get("ab_update") == "true":
625 # Unlike otacerts.zip that may contain multiple keys, we can only specify
626 # ONE payload verification key.
627 if len(mapped_keys) > 1:
628 print("\n WARNING: Found more than one OTA keys; Using the first one"
629 " as payload verification key.\n\n")
630
Tao Bao0c28d2d2017-12-24 10:37:38 -0800631 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800632 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700633 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700634 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700635 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
636 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700637 common.ZipWriteStr(
638 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700639 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700640 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700641
Doug Zongker412c02f2014-02-13 10:58:24 -0800642 return new_recovery_keys
643
Tao Bao8adcfd12016-06-17 17:01:22 -0700644
Tao Bao0c28d2d2017-12-24 10:37:38 -0800645def ReplaceVerityPublicKey(output_zip, filename, key_path):
646 """Replaces the verity public key at the given path in the given zip.
647
648 Args:
649 output_zip: The output target_files zip.
650 filename: The archive name in the output zip.
651 key_path: The path to the public key.
652 """
653 print("Replacing verity public key with %s" % (key_path,))
654 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700655
Tao Bao8adcfd12016-06-17 17:01:22 -0700656
Tao Bao46a59992017-06-05 11:55:16 -0700657def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800658 """Replaces the verity private key in misc_info dict.
659
660 Args:
661 misc_info: The info dict.
662 key_path: The path to the private key in PKCS#8 format.
663 """
664 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700665 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700666
Tao Bao8adcfd12016-06-17 17:01:22 -0700667
Tao Baoe838d142017-12-23 23:44:48 -0800668def ReplaceVerityKeyId(input_zip, output_zip, key_path):
669 """Replaces the veritykeyid parameter in BOOT/cmdline.
670
671 Args:
672 input_zip: The input target_files zip, which should be already open.
673 output_zip: The output target_files zip, which should be already open and
674 writable.
675 key_path: The path to the PEM encoded X.509 certificate.
676 """
677 in_cmdline = input_zip.read("BOOT/cmdline")
678 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700679 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800680 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
681 return
682
Tao Bao0c28d2d2017-12-24 10:37:38 -0800683 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700684 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800685 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800686 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800687 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700688
Tao Baoe838d142017-12-23 23:44:48 -0800689 # Extract keyid using openssl command.
690 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800691 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800692 keyid, stderr = p.communicate()
693 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
694 keyid = re.search(
695 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
696 print("Replacing verity keyid with {}".format(keyid))
697 out_buffer.append("veritykeyid=id:%s" % (keyid,))
698
699 out_cmdline = ' '.join(out_buffer).strip() + '\n'
700 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700701
702
703def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
704 """Replaces META/misc_info.txt.
705
706 Only writes back the ones in the original META/misc_info.txt. Because the
707 current in-memory dict contains additional items computed at runtime.
708 """
709 misc_info_old = common.LoadDictionaryFromLines(
710 input_zip.read('META/misc_info.txt').split('\n'))
711 items = []
712 for key in sorted(misc_info):
713 if key in misc_info_old:
714 items.append('%s=%s' % (key, misc_info[key]))
715 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700716
Tao Bao8adcfd12016-06-17 17:01:22 -0700717
Tao Bao639118f2017-06-19 15:48:02 -0700718def ReplaceAvbSigningKeys(misc_info):
719 """Replaces the AVB signing keys."""
720
721 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800722 'boot' : 'avb_boot_add_hash_footer_args',
723 'dtbo' : 'avb_dtbo_add_hash_footer_args',
724 'recovery' : 'avb_recovery_add_hash_footer_args',
725 'system' : 'avb_system_add_hashtree_footer_args',
726 'vendor' : 'avb_vendor_add_hashtree_footer_args',
727 'vbmeta' : 'avb_vbmeta_args',
Tao Bao639118f2017-06-19 15:48:02 -0700728 }
729
730 def ReplaceAvbPartitionSigningKey(partition):
731 key = OPTIONS.avb_keys.get(partition)
732 if not key:
733 return
734
735 algorithm = OPTIONS.avb_algorithms.get(partition)
736 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
737
Tao Bao0c28d2d2017-12-24 10:37:38 -0800738 print('Replacing AVB signing key for %s with "%s" (%s)' % (
739 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700740 misc_info['avb_' + partition + '_algorithm'] = algorithm
741 misc_info['avb_' + partition + '_key_path'] = key
742
743 extra_args = OPTIONS.avb_extra_args.get(partition)
744 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800745 print('Setting extra AVB signing args for %s to "%s"' % (
746 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700747 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
748 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
749
750 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
751 ReplaceAvbPartitionSigningKey(partition)
752
753
Doug Zongker831840e2011-09-22 10:28:04 -0700754def BuildKeyMap(misc_info, key_mapping_options):
755 for s, d in key_mapping_options:
756 if s is None: # -d option
757 devkey = misc_info.get("default_system_dev_certificate",
758 "build/target/product/security/testkey")
759 devkeydir = os.path.dirname(devkey)
760
761 OPTIONS.key_map.update({
762 devkeydir + "/testkey": d + "/releasekey",
763 devkeydir + "/devkey": d + "/releasekey",
764 devkeydir + "/media": d + "/media",
765 devkeydir + "/shared": d + "/shared",
766 devkeydir + "/platform": d + "/platform",
767 })
768 else:
769 OPTIONS.key_map[s] = d
770
771
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800772def GetApiLevelAndCodename(input_tf_zip):
773 data = input_tf_zip.read("SYSTEM/build.prop")
774 api_level = None
775 codename = None
776 for line in data.split("\n"):
777 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800778 if line and line[0] != '#' and "=" in line:
779 key, value = line.split("=", 1)
780 key = key.strip()
781 if key == "ro.build.version.sdk":
782 api_level = int(value.strip())
783 elif key == "ro.build.version.codename":
784 codename = value.strip()
785
786 if api_level is None:
787 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
788 if codename is None:
789 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
790
791 return (api_level, codename)
792
793
794def GetCodenameToApiLevelMap(input_tf_zip):
795 data = input_tf_zip.read("SYSTEM/build.prop")
796 api_level = None
797 codenames = None
798 for line in data.split("\n"):
799 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800800 if line and line[0] != '#' and "=" in line:
801 key, value = line.split("=", 1)
802 key = key.strip()
803 if key == "ro.build.version.sdk":
804 api_level = int(value.strip())
805 elif key == "ro.build.version.all_codenames":
806 codenames = value.strip().split(",")
807
808 if api_level is None:
809 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
810 if codenames is None:
811 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
812
813 result = dict()
814 for codename in codenames:
815 codename = codename.strip()
816 if len(codename) > 0:
817 result[codename] = api_level
818 return result
819
820
Doug Zongkereef39442009-04-02 12:14:19 -0700821def main(argv):
822
Doug Zongker831840e2011-09-22 10:28:04 -0700823 key_mapping_options = []
824
Doug Zongkereef39442009-04-02 12:14:19 -0700825 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -0700826 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -0700827 names, key = a.split("=")
828 names = names.split(",")
829 for n in names:
830 OPTIONS.extra_apks[n] = key
Tao Bao93c2a012018-06-19 12:19:35 -0700831 elif o == "--skip_apks_with_path_prefix":
832 # Sanity check the prefix, which must be in all upper case.
833 prefix = a.split('/')[0]
834 if not prefix or prefix != prefix.upper():
835 raise ValueError("Invalid path prefix '%s'" % (a,))
836 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700837 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -0700838 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -0700839 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -0700840 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700841 elif o in ("-o", "--replace_ota_keys"):
842 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -0700843 elif o in ("-t", "--tag_changes"):
844 new = []
845 for i in a.split(","):
846 i = i.strip()
847 if not i or i[0] not in "-+":
848 raise ValueError("Bad tag change '%s'" % (i,))
849 new.append(i[0] + i[1:].strip())
850 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -0700851 elif o == "--replace_verity_public_key":
852 OPTIONS.replace_verity_public_key = (True, a)
853 elif o == "--replace_verity_private_key":
854 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700855 elif o == "--replace_verity_keyid":
856 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -0700857 elif o == "--avb_vbmeta_key":
858 OPTIONS.avb_keys['vbmeta'] = a
859 elif o == "--avb_vbmeta_algorithm":
860 OPTIONS.avb_algorithms['vbmeta'] = a
861 elif o == "--avb_vbmeta_extra_args":
862 OPTIONS.avb_extra_args['vbmeta'] = a
863 elif o == "--avb_boot_key":
864 OPTIONS.avb_keys['boot'] = a
865 elif o == "--avb_boot_algorithm":
866 OPTIONS.avb_algorithms['boot'] = a
867 elif o == "--avb_boot_extra_args":
868 OPTIONS.avb_extra_args['boot'] = a
869 elif o == "--avb_dtbo_key":
870 OPTIONS.avb_keys['dtbo'] = a
871 elif o == "--avb_dtbo_algorithm":
872 OPTIONS.avb_algorithms['dtbo'] = a
873 elif o == "--avb_dtbo_extra_args":
874 OPTIONS.avb_extra_args['dtbo'] = a
875 elif o == "--avb_system_key":
876 OPTIONS.avb_keys['system'] = a
877 elif o == "--avb_system_algorithm":
878 OPTIONS.avb_algorithms['system'] = a
879 elif o == "--avb_system_extra_args":
880 OPTIONS.avb_extra_args['system'] = a
881 elif o == "--avb_vendor_key":
882 OPTIONS.avb_keys['vendor'] = a
883 elif o == "--avb_vendor_algorithm":
884 OPTIONS.avb_algorithms['vendor'] = a
885 elif o == "--avb_vendor_extra_args":
886 OPTIONS.avb_extra_args['vendor'] = a
Doug Zongkereef39442009-04-02 12:14:19 -0700887 else:
888 return False
889 return True
890
Tao Bao639118f2017-06-19 15:48:02 -0700891 args = common.ParseOptions(
892 argv, __doc__,
893 extra_opts="e:d:k:ot:",
894 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -0800895 "extra_apks=",
Tao Bao93c2a012018-06-19 12:19:35 -0700896 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -0800897 "default_key_mappings=",
898 "key_mapping=",
899 "replace_ota_keys",
900 "tag_changes=",
901 "replace_verity_public_key=",
902 "replace_verity_private_key=",
903 "replace_verity_keyid=",
904 "avb_vbmeta_algorithm=",
905 "avb_vbmeta_key=",
906 "avb_vbmeta_extra_args=",
907 "avb_boot_algorithm=",
908 "avb_boot_key=",
909 "avb_boot_extra_args=",
910 "avb_dtbo_algorithm=",
911 "avb_dtbo_key=",
912 "avb_dtbo_extra_args=",
913 "avb_system_algorithm=",
914 "avb_system_key=",
915 "avb_system_extra_args=",
916 "avb_vendor_algorithm=",
917 "avb_vendor_key=",
918 "avb_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -0700919 ],
920 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -0700921
922 if len(args) != 2:
923 common.Usage(__doc__)
924 sys.exit(1)
925
926 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -0700927 output_zip = zipfile.ZipFile(args[1], "w",
928 compression=zipfile.ZIP_DEFLATED,
929 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700930
Doug Zongker831840e2011-09-22 10:28:04 -0700931 misc_info = common.LoadInfoDict(input_zip)
932
933 BuildKeyMap(misc_info, key_mapping_options)
934
Narayan Kamatha07bf042017-08-14 14:49:21 +0100935 certmap, compressed_extension = common.ReadApkCerts(input_zip)
936 apk_key_map = GetApkCerts(certmap)
937 CheckAllApksSigned(input_zip, apk_key_map, compressed_extension)
Doug Zongkereb338ef2009-05-20 16:50:49 -0700938
939 key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
Tao Bao9aa4b9b2016-09-29 17:53:56 -0700940 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800941 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800942
Doug Zongker412c02f2014-02-13 10:58:24 -0800943 ProcessTargetFiles(input_zip, output_zip, misc_info,
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800944 apk_key_map, key_passwords,
945 platform_api_level,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100946 codename_to_api_level_map,
947 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700948
Tao Bao2ed665a2015-04-01 11:21:55 -0700949 common.ZipClose(input_zip)
950 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700951
Tianjie Xub48589a2016-08-03 19:21:52 -0700952 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700953 new_args = ["--is_signing"]
954 # add_img_to_target_files builds the system image from scratch, so the
955 # recovery patch is guaranteed to be regenerated there.
956 if OPTIONS.rebuild_recovery:
957 new_args.append("--rebuild_recovery")
958 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -0700959 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -0700960
Tao Bao0c28d2d2017-12-24 10:37:38 -0800961 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -0700962
963
964if __name__ == '__main__':
965 try:
966 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -0800967 except common.ExternalError as e:
968 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -0700969 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -0700970 finally:
971 common.Cleanup()