blob: a07f67fd4c291f6e3c4fce87deda683ae7df5156 [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 Bao33bf2682019-01-11 12:37:35 -0800312 # Skip split super images, which will be re-generated during signing.
313 if filename.startswith("OTA/") and filename.endswith(".img"):
314 continue
315
Tao Bao11f955c2018-06-19 12:19:35 -0700316 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700317 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700318 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
319 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
320
321 if is_apk and should_be_skipped:
322 # Copy skipped APKs verbatim.
323 print(
324 "NOT signing: %s\n"
325 " (skipped due to matching prefix)" % (filename,))
326 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800327
Tao Baof2cffbd2015-07-22 12:33:18 -0700328 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700329 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700330 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100331 if is_compressed:
332 name = name[:-len(compressed_extension)]
333
Doug Zongker43874f82009-04-14 14:05:15 -0700334 key = apk_key_map[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800335 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800336 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800337 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800338 codename_to_api_level_map, is_compressed)
Tao Bao2ed665a2015-04-01 11:21:55 -0700339 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700340 else:
341 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700342 print(
343 "NOT signing: %s\n"
344 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700345 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700346
347 # System properties.
Tao Bao11f955c2018-06-19 12:19:35 -0700348 elif filename in ("SYSTEM/build.prop",
349 "VENDOR/build.prop",
350 "SYSTEM/etc/prop.default",
351 "BOOT/RAMDISK/prop.default",
352 "BOOT/RAMDISK/default.prop", # legacy
353 "ROOT/default.prop", # legacy
354 "RECOVERY/RAMDISK/prop.default",
355 "RECOVERY/RAMDISK/default.prop"): # legacy
356 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800357 if stat.S_ISLNK(info.external_attr >> 16):
358 new_data = data
359 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800360 new_data = RewriteProps(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700361 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700362
Tao Bao66472632017-12-04 17:16:36 -0800363 # Replace the certs in *mac_permissions.xml (there could be multiple, such
364 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700365 elif filename.endswith("mac_permissions.xml"):
366 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400367 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700368 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700369
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700370 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700371 elif filename in ("SYSTEM/recovery-from-boot.p",
372 "SYSTEM/etc/recovery.img",
373 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700374 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700375
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700376 # Don't copy OTA certs if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700377 elif (
378 OPTIONS.replace_ota_keys and
379 filename in (
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700380 "BOOT/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700381 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700382 "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700383 "SYSTEM/etc/security/otacerts.zip",
384 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800385 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700386
Tao Bao46a59992017-06-05 11:55:16 -0700387 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700388 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700389 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700390
391 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700392 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700393 filename in ("BOOT/RAMDISK/verity_key",
394 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700395 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700396
Tao Bao8adcfd12016-06-17 17:01:22 -0700397 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700398 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700399 pass
400
Tianjie Xu4f099002016-08-11 18:04:27 -0700401 # Skip the care_map as we will regenerate the system/vendor images.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700402 elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700403 pass
404
Tao Baoa80ed222016-06-16 14:41:24 -0700405 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700406 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700407 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700408
Doug Zongker412c02f2014-02-13 10:58:24 -0800409 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700410 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800411
Tao Bao46a59992017-06-05 11:55:16 -0700412 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700413 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700414 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700415
416 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800417 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700418 # We are replacing the one in boot image only, since the one under
419 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700420 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700421 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700422
423 # Replace the keyid string in BOOT/cmdline.
424 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700425 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
426 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800427
Tao Bao639118f2017-06-19 15:48:02 -0700428 # Replace the AVB signing keys, if any.
429 ReplaceAvbSigningKeys(misc_info)
430
Tao Bao46a59992017-06-05 11:55:16 -0700431 # Write back misc_info with the latest values.
432 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
433
Doug Zongker8e931bf2009-04-06 15:21:45 -0700434
Robert Craig817c5742013-04-19 10:59:22 -0400435def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800436 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400437
Tao Bao66472632017-12-04 17:16:36 -0800438 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
439 be skipped. After the replacement, it additionally checks for duplicate
440 entries, which would otherwise fail the policy loading code in
441 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
442
443 Args:
444 data: Input string that contains a set of X.509 certs.
445
446 Returns:
447 A string after the replacement.
448
449 Raises:
450 AssertionError: On finding duplicate entries.
451 """
452 for old, new in OPTIONS.key_map.iteritems():
453 if OPTIONS.verbose:
454 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
455
456 try:
457 with open(old + ".x509.pem") as old_fp:
458 old_cert16 = base64.b16encode(
459 common.ParseCertificate(old_fp.read())).lower()
460 with open(new + ".x509.pem") as new_fp:
461 new_cert16 = base64.b16encode(
462 common.ParseCertificate(new_fp.read())).lower()
463 except IOError as e:
464 if OPTIONS.verbose or e.errno != errno.ENOENT:
465 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
466 "%s.x509.pem." % (e.filename, e.strerror, old, new))
467 continue
468
469 # Only match entire certs.
470 pattern = "\\b" + old_cert16 + "\\b"
471 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
472
473 if OPTIONS.verbose:
474 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
475 num, old, new))
476
477 # Verify that there're no duplicate entries after the replacement. Note that
478 # it's only checking entries with global seinfo at the moment (i.e. ignoring
479 # the ones with inner packages). (Bug: 69479366)
480 root = ElementTree.fromstring(data)
481 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
482 assert len(signatures) == len(set(signatures)), \
483 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400484
485 return data
486
487
Doug Zongkerc09abc82010-01-11 13:09:15 -0800488def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800489 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
490
491 Args:
492 tags: The input string that contains comma-separated tags.
493
494 Returns:
495 The updated tags (comma-separated and sorted).
496 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800497 tags = set(tags.split(","))
498 for ch in OPTIONS.tag_changes:
499 if ch[0] == "-":
500 tags.discard(ch[1:])
501 elif ch[0] == "+":
502 tags.add(ch[1:])
503 return ",".join(sorted(tags))
504
505
Tao Baoa7054ee2017-12-08 14:42:16 -0800506def RewriteProps(data):
507 """Rewrites the system properties in the given string.
508
509 Each property is expected in 'key=value' format. The properties that contain
510 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
511 EditTags().
512
513 Args:
514 data: Input string, separated by newlines.
515
516 Returns:
517 The string with modified properties.
518 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700519 output = []
520 for line in data.split("\n"):
521 line = line.strip()
522 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700523 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700524 key, value = line.split("=", 1)
Tao Baoa7054ee2017-12-08 14:42:16 -0800525 if key in ("ro.build.fingerprint", "ro.build.thumbprint",
526 "ro.vendor.build.fingerprint", "ro.vendor.build.thumbprint"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800527 pieces = value.split("/")
528 pieces[-1] = EditTags(pieces[-1])
529 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700530 elif key == "ro.bootimage.build.fingerprint":
531 pieces = value.split("/")
532 pieces[-1] = EditTags(pieces[-1])
533 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700534 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800535 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700536 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800537 pieces[-1] = EditTags(pieces[-1])
538 value = " ".join(pieces)
539 elif key == "ro.build.tags":
540 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700541 elif key == "ro.build.display.id":
542 # change, eg, "JWR66N dev-keys" to "JWR66N"
543 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700544 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800545 value.pop()
546 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800547 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700548 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800549 print(" replace: ", original_line)
550 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700551 output.append(line)
552 return "\n".join(output) + "\n"
553
554
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700555def WriteOtacerts(output_zip, filename, keys):
556 """Constructs a zipfile from given keys; and writes it to output_zip.
557
558 Args:
559 output_zip: The output target_files zip.
560 filename: The archive name in the output zip.
561 keys: A list of public keys to use during OTA package verification.
562 """
563
564 try:
565 from StringIO import StringIO
566 except ImportError:
567 from io import StringIO
568 temp_file = StringIO()
569 certs_zip = zipfile.ZipFile(temp_file, "w")
570 for k in keys:
571 common.ZipWrite(certs_zip, k)
572 common.ZipClose(certs_zip)
573 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
574
575
Doug Zongker831840e2011-09-22 10:28:04 -0700576def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700577 try:
578 keylist = input_tf_zip.read("META/otakeys.txt").split()
579 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700580 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700581
Tao Baof718f902017-11-09 10:10:10 -0800582 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800583 if extra_recovery_keys:
584 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
585 for k in extra_recovery_keys.split()]
586 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800587 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800588 else:
589 extra_recovery_keys = []
590
Doug Zongker8e931bf2009-04-06 15:21:45 -0700591 mapped_keys = []
592 for k in keylist:
593 m = re.match(r"^(.*)\.x509\.pem$", k)
594 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800595 raise common.ExternalError(
596 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700597 k = m.group(1)
598 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
599
Doug Zongkere05628c2009-08-20 17:38:42 -0700600 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800601 print("using:\n ", "\n ".join(mapped_keys))
602 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700603 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700604 devkey = misc_info.get("default_system_dev_certificate",
605 "build/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800606 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
607 if mapped_devkey != devkey:
608 misc_info["default_system_dev_certificate"] = mapped_devkey
609 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700610 print("META/otakeys.txt has no keys; using %s for OTA package"
611 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700612
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700613 # recovery now uses the same x509.pem version of the keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800614 # extra_recovery_keys are used only in recovery.
Tom Cherry2929cad2018-09-20 11:04:37 -0700615 if misc_info.get("recovery_as_boot") == "true":
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700616 recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
Tao Baoa80ed222016-06-16 14:41:24 -0700617 else:
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700618 recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
619
620 WriteOtacerts(output_tf_zip, recovery_keys_location,
621 mapped_keys + extra_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700622
623 # SystemUpdateActivity uses the x509.pem version of the keys, but
624 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800625 # We DO NOT include the extra_recovery_keys (if any) here.
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700626 WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700627
Tao Baoa80ed222016-06-16 14:41:24 -0700628 # For A/B devices, update the payload verification key.
629 if misc_info.get("ab_update") == "true":
630 # Unlike otacerts.zip that may contain multiple keys, we can only specify
631 # ONE payload verification key.
632 if len(mapped_keys) > 1:
633 print("\n WARNING: Found more than one OTA keys; Using the first one"
634 " as payload verification key.\n\n")
635
Tao Bao0c28d2d2017-12-24 10:37:38 -0800636 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800637 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700638 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700639 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700640 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
641 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700642 common.ZipWriteStr(
643 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700644 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700645 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700646
Tao Bao8adcfd12016-06-17 17:01:22 -0700647
Tao Bao0c28d2d2017-12-24 10:37:38 -0800648def ReplaceVerityPublicKey(output_zip, filename, key_path):
649 """Replaces the verity public key at the given path in the given zip.
650
651 Args:
652 output_zip: The output target_files zip.
653 filename: The archive name in the output zip.
654 key_path: The path to the public key.
655 """
656 print("Replacing verity public key with %s" % (key_path,))
657 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700658
Tao Bao8adcfd12016-06-17 17:01:22 -0700659
Tao Bao46a59992017-06-05 11:55:16 -0700660def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800661 """Replaces the verity private key in misc_info dict.
662
663 Args:
664 misc_info: The info dict.
665 key_path: The path to the private key in PKCS#8 format.
666 """
667 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700668 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700669
Tao Bao8adcfd12016-06-17 17:01:22 -0700670
Tao Baoe838d142017-12-23 23:44:48 -0800671def ReplaceVerityKeyId(input_zip, output_zip, key_path):
672 """Replaces the veritykeyid parameter in BOOT/cmdline.
673
674 Args:
675 input_zip: The input target_files zip, which should be already open.
676 output_zip: The output target_files zip, which should be already open and
677 writable.
678 key_path: The path to the PEM encoded X.509 certificate.
679 """
680 in_cmdline = input_zip.read("BOOT/cmdline")
681 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700682 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800683 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
684 return
685
Tao Bao0c28d2d2017-12-24 10:37:38 -0800686 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700687 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800688 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800689 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800690 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700691
Tao Baoe838d142017-12-23 23:44:48 -0800692 # Extract keyid using openssl command.
693 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800694 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800695 keyid, stderr = p.communicate()
696 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
697 keyid = re.search(
698 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
699 print("Replacing verity keyid with {}".format(keyid))
700 out_buffer.append("veritykeyid=id:%s" % (keyid,))
701
702 out_cmdline = ' '.join(out_buffer).strip() + '\n'
703 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700704
705
706def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
707 """Replaces META/misc_info.txt.
708
709 Only writes back the ones in the original META/misc_info.txt. Because the
710 current in-memory dict contains additional items computed at runtime.
711 """
712 misc_info_old = common.LoadDictionaryFromLines(
713 input_zip.read('META/misc_info.txt').split('\n'))
714 items = []
715 for key in sorted(misc_info):
716 if key in misc_info_old:
717 items.append('%s=%s' % (key, misc_info[key]))
718 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700719
Tao Bao8adcfd12016-06-17 17:01:22 -0700720
Tao Bao639118f2017-06-19 15:48:02 -0700721def ReplaceAvbSigningKeys(misc_info):
722 """Replaces the AVB signing keys."""
723
724 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800725 'boot' : 'avb_boot_add_hash_footer_args',
726 'dtbo' : 'avb_dtbo_add_hash_footer_args',
727 'recovery' : 'avb_recovery_add_hash_footer_args',
728 'system' : 'avb_system_add_hashtree_footer_args',
729 'vendor' : 'avb_vendor_add_hashtree_footer_args',
730 'vbmeta' : 'avb_vbmeta_args',
Tao Bao639118f2017-06-19 15:48:02 -0700731 }
732
733 def ReplaceAvbPartitionSigningKey(partition):
734 key = OPTIONS.avb_keys.get(partition)
735 if not key:
736 return
737
738 algorithm = OPTIONS.avb_algorithms.get(partition)
739 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
740
Tao Bao0c28d2d2017-12-24 10:37:38 -0800741 print('Replacing AVB signing key for %s with "%s" (%s)' % (
742 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700743 misc_info['avb_' + partition + '_algorithm'] = algorithm
744 misc_info['avb_' + partition + '_key_path'] = key
745
746 extra_args = OPTIONS.avb_extra_args.get(partition)
747 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800748 print('Setting extra AVB signing args for %s to "%s"' % (
749 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700750 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
751 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
752
753 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
754 ReplaceAvbPartitionSigningKey(partition)
755
756
Doug Zongker831840e2011-09-22 10:28:04 -0700757def BuildKeyMap(misc_info, key_mapping_options):
758 for s, d in key_mapping_options:
759 if s is None: # -d option
760 devkey = misc_info.get("default_system_dev_certificate",
761 "build/target/product/security/testkey")
762 devkeydir = os.path.dirname(devkey)
763
764 OPTIONS.key_map.update({
765 devkeydir + "/testkey": d + "/releasekey",
766 devkeydir + "/devkey": d + "/releasekey",
767 devkeydir + "/media": d + "/media",
768 devkeydir + "/shared": d + "/shared",
769 devkeydir + "/platform": d + "/platform",
770 })
771 else:
772 OPTIONS.key_map[s] = d
773
774
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800775def GetApiLevelAndCodename(input_tf_zip):
776 data = input_tf_zip.read("SYSTEM/build.prop")
777 api_level = None
778 codename = None
779 for line in data.split("\n"):
780 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800781 if line and line[0] != '#' and "=" in line:
782 key, value = line.split("=", 1)
783 key = key.strip()
784 if key == "ro.build.version.sdk":
785 api_level = int(value.strip())
786 elif key == "ro.build.version.codename":
787 codename = value.strip()
788
789 if api_level is None:
790 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
791 if codename is None:
792 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
793
794 return (api_level, codename)
795
796
797def GetCodenameToApiLevelMap(input_tf_zip):
798 data = input_tf_zip.read("SYSTEM/build.prop")
799 api_level = None
800 codenames = None
801 for line in data.split("\n"):
802 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800803 if line and line[0] != '#' and "=" in line:
804 key, value = line.split("=", 1)
805 key = key.strip()
806 if key == "ro.build.version.sdk":
807 api_level = int(value.strip())
808 elif key == "ro.build.version.all_codenames":
809 codenames = value.strip().split(",")
810
811 if api_level is None:
812 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
813 if codenames is None:
814 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
815
816 result = dict()
817 for codename in codenames:
818 codename = codename.strip()
819 if len(codename) > 0:
820 result[codename] = api_level
821 return result
822
823
Doug Zongkereef39442009-04-02 12:14:19 -0700824def main(argv):
825
Doug Zongker831840e2011-09-22 10:28:04 -0700826 key_mapping_options = []
827
Doug Zongkereef39442009-04-02 12:14:19 -0700828 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -0700829 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -0700830 names, key = a.split("=")
831 names = names.split(",")
832 for n in names:
833 OPTIONS.extra_apks[n] = key
Tao Bao93c2a012018-06-19 12:19:35 -0700834 elif o == "--skip_apks_with_path_prefix":
835 # Sanity check the prefix, which must be in all upper case.
836 prefix = a.split('/')[0]
837 if not prefix or prefix != prefix.upper():
838 raise ValueError("Invalid path prefix '%s'" % (a,))
839 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700840 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -0700841 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -0700842 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -0700843 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700844 elif o in ("-o", "--replace_ota_keys"):
845 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -0700846 elif o in ("-t", "--tag_changes"):
847 new = []
848 for i in a.split(","):
849 i = i.strip()
850 if not i or i[0] not in "-+":
851 raise ValueError("Bad tag change '%s'" % (i,))
852 new.append(i[0] + i[1:].strip())
853 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -0700854 elif o == "--replace_verity_public_key":
855 OPTIONS.replace_verity_public_key = (True, a)
856 elif o == "--replace_verity_private_key":
857 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700858 elif o == "--replace_verity_keyid":
859 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -0700860 elif o == "--avb_vbmeta_key":
861 OPTIONS.avb_keys['vbmeta'] = a
862 elif o == "--avb_vbmeta_algorithm":
863 OPTIONS.avb_algorithms['vbmeta'] = a
864 elif o == "--avb_vbmeta_extra_args":
865 OPTIONS.avb_extra_args['vbmeta'] = a
866 elif o == "--avb_boot_key":
867 OPTIONS.avb_keys['boot'] = a
868 elif o == "--avb_boot_algorithm":
869 OPTIONS.avb_algorithms['boot'] = a
870 elif o == "--avb_boot_extra_args":
871 OPTIONS.avb_extra_args['boot'] = a
872 elif o == "--avb_dtbo_key":
873 OPTIONS.avb_keys['dtbo'] = a
874 elif o == "--avb_dtbo_algorithm":
875 OPTIONS.avb_algorithms['dtbo'] = a
876 elif o == "--avb_dtbo_extra_args":
877 OPTIONS.avb_extra_args['dtbo'] = a
878 elif o == "--avb_system_key":
879 OPTIONS.avb_keys['system'] = a
880 elif o == "--avb_system_algorithm":
881 OPTIONS.avb_algorithms['system'] = a
882 elif o == "--avb_system_extra_args":
883 OPTIONS.avb_extra_args['system'] = a
884 elif o == "--avb_vendor_key":
885 OPTIONS.avb_keys['vendor'] = a
886 elif o == "--avb_vendor_algorithm":
887 OPTIONS.avb_algorithms['vendor'] = a
888 elif o == "--avb_vendor_extra_args":
889 OPTIONS.avb_extra_args['vendor'] = a
Doug Zongkereef39442009-04-02 12:14:19 -0700890 else:
891 return False
892 return True
893
Tao Bao639118f2017-06-19 15:48:02 -0700894 args = common.ParseOptions(
895 argv, __doc__,
896 extra_opts="e:d:k:ot:",
897 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -0800898 "extra_apks=",
Tao Bao93c2a012018-06-19 12:19:35 -0700899 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -0800900 "default_key_mappings=",
901 "key_mapping=",
902 "replace_ota_keys",
903 "tag_changes=",
904 "replace_verity_public_key=",
905 "replace_verity_private_key=",
906 "replace_verity_keyid=",
907 "avb_vbmeta_algorithm=",
908 "avb_vbmeta_key=",
909 "avb_vbmeta_extra_args=",
910 "avb_boot_algorithm=",
911 "avb_boot_key=",
912 "avb_boot_extra_args=",
913 "avb_dtbo_algorithm=",
914 "avb_dtbo_key=",
915 "avb_dtbo_extra_args=",
916 "avb_system_algorithm=",
917 "avb_system_key=",
918 "avb_system_extra_args=",
919 "avb_vendor_algorithm=",
920 "avb_vendor_key=",
921 "avb_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -0700922 ],
923 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -0700924
925 if len(args) != 2:
926 common.Usage(__doc__)
927 sys.exit(1)
928
929 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -0700930 output_zip = zipfile.ZipFile(args[1], "w",
931 compression=zipfile.ZIP_DEFLATED,
932 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700933
Doug Zongker831840e2011-09-22 10:28:04 -0700934 misc_info = common.LoadInfoDict(input_zip)
935
936 BuildKeyMap(misc_info, key_mapping_options)
937
Narayan Kamatha07bf042017-08-14 14:49:21 +0100938 certmap, compressed_extension = common.ReadApkCerts(input_zip)
939 apk_key_map = GetApkCerts(certmap)
940 CheckAllApksSigned(input_zip, apk_key_map, compressed_extension)
Doug Zongkereb338ef2009-05-20 16:50:49 -0700941
942 key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
Tao Bao9aa4b9b2016-09-29 17:53:56 -0700943 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800944 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800945
Doug Zongker412c02f2014-02-13 10:58:24 -0800946 ProcessTargetFiles(input_zip, output_zip, misc_info,
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800947 apk_key_map, key_passwords,
948 platform_api_level,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100949 codename_to_api_level_map,
950 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700951
Tao Bao2ed665a2015-04-01 11:21:55 -0700952 common.ZipClose(input_zip)
953 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700954
Tianjie Xub48589a2016-08-03 19:21:52 -0700955 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700956 new_args = ["--is_signing"]
957 # add_img_to_target_files builds the system image from scratch, so the
958 # recovery patch is guaranteed to be regenerated there.
959 if OPTIONS.rebuild_recovery:
960 new_args.append("--rebuild_recovery")
961 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -0700962 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -0700963
Tao Bao0c28d2d2017-12-24 10:37:38 -0800964 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -0700965
966
967if __name__ == '__main__':
968 try:
969 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -0800970 except common.ExternalError as e:
971 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -0700972 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -0700973 finally:
974 common.Cleanup()