blob: de3ead61c84249b042d751927a4f2bbff64c759b [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
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700372 # Don't copy OTA certs if we're replacing them.
Tao Bao696bb332018-08-17 16:27:01 -0700373 elif (
374 OPTIONS.replace_ota_keys and
375 filename in (
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700376 "BOOT/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700377 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700378 "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
Tao Bao696bb332018-08-17 16:27:01 -0700379 "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.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700398 elif filename == "META/care_map.pb" or 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
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700551def WriteOtacerts(output_zip, filename, keys):
552 """Constructs a zipfile from given keys; and writes it to output_zip.
553
554 Args:
555 output_zip: The output target_files zip.
556 filename: The archive name in the output zip.
557 keys: A list of public keys to use during OTA package verification.
558 """
559
560 try:
561 from StringIO import StringIO
562 except ImportError:
563 from io import StringIO
564 temp_file = StringIO()
565 certs_zip = zipfile.ZipFile(temp_file, "w")
566 for k in keys:
567 common.ZipWrite(certs_zip, k)
568 common.ZipClose(certs_zip)
569 common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
570
571
Doug Zongker831840e2011-09-22 10:28:04 -0700572def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700573 try:
574 keylist = input_tf_zip.read("META/otakeys.txt").split()
575 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700576 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700577
Tao Baof718f902017-11-09 10:10:10 -0800578 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800579 if extra_recovery_keys:
580 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
581 for k in extra_recovery_keys.split()]
582 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800583 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800584 else:
585 extra_recovery_keys = []
586
Doug Zongker8e931bf2009-04-06 15:21:45 -0700587 mapped_keys = []
588 for k in keylist:
589 m = re.match(r"^(.*)\.x509\.pem$", k)
590 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800591 raise common.ExternalError(
592 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700593 k = m.group(1)
594 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
595
Doug Zongkere05628c2009-08-20 17:38:42 -0700596 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800597 print("using:\n ", "\n ".join(mapped_keys))
598 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700599 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700600 devkey = misc_info.get("default_system_dev_certificate",
601 "build/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800602 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
603 if mapped_devkey != devkey:
604 misc_info["default_system_dev_certificate"] = mapped_devkey
605 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700606 print("META/otakeys.txt has no keys; using %s for OTA package"
607 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700608
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700609 # recovery now uses the same x509.pem version of the keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800610 # extra_recovery_keys are used only in recovery.
Tom Cherry2929cad2018-09-20 11:04:37 -0700611 if misc_info.get("recovery_as_boot") == "true":
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700612 recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip"
Tao Baoa80ed222016-06-16 14:41:24 -0700613 else:
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700614 recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip"
615
616 WriteOtacerts(output_tf_zip, recovery_keys_location,
617 mapped_keys + extra_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700618
619 # SystemUpdateActivity uses the x509.pem version of the keys, but
620 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800621 # We DO NOT include the extra_recovery_keys (if any) here.
Tianjie Xuffbe6b92018-10-19 14:34:15 -0700622 WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys)
Doug Zongkereef39442009-04-02 12:14:19 -0700623
Tao Baoa80ed222016-06-16 14:41:24 -0700624 # For A/B devices, update the payload verification key.
625 if misc_info.get("ab_update") == "true":
626 # Unlike otacerts.zip that may contain multiple keys, we can only specify
627 # ONE payload verification key.
628 if len(mapped_keys) > 1:
629 print("\n WARNING: Found more than one OTA keys; Using the first one"
630 " as payload verification key.\n\n")
631
Tao Bao0c28d2d2017-12-24 10:37:38 -0800632 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800633 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700634 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700635 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700636 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
637 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700638 common.ZipWriteStr(
639 output_tf_zip,
Tao Bao696bb332018-08-17 16:27:01 -0700640 "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700641 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700642
Tao Bao8adcfd12016-06-17 17:01:22 -0700643
Tao Bao0c28d2d2017-12-24 10:37:38 -0800644def ReplaceVerityPublicKey(output_zip, filename, key_path):
645 """Replaces the verity public key at the given path in the given zip.
646
647 Args:
648 output_zip: The output target_files zip.
649 filename: The archive name in the output zip.
650 key_path: The path to the public key.
651 """
652 print("Replacing verity public key with %s" % (key_path,))
653 common.ZipWrite(output_zip, key_path, arcname=filename)
Geremy Condraf19b3652014-07-29 17:54:54 -0700654
Tao Bao8adcfd12016-06-17 17:01:22 -0700655
Tao Bao46a59992017-06-05 11:55:16 -0700656def ReplaceVerityPrivateKey(misc_info, key_path):
Tao Bao0c28d2d2017-12-24 10:37:38 -0800657 """Replaces the verity private key in misc_info dict.
658
659 Args:
660 misc_info: The info dict.
661 key_path: The path to the private key in PKCS#8 format.
662 """
663 print("Replacing verity private key with %s" % (key_path,))
Andrew Boied083f0b2014-09-15 16:01:07 -0700664 misc_info["verity_key"] = key_path
Doug Zongkereef39442009-04-02 12:14:19 -0700665
Tao Bao8adcfd12016-06-17 17:01:22 -0700666
Tao Baoe838d142017-12-23 23:44:48 -0800667def ReplaceVerityKeyId(input_zip, output_zip, key_path):
668 """Replaces the veritykeyid parameter in BOOT/cmdline.
669
670 Args:
671 input_zip: The input target_files zip, which should be already open.
672 output_zip: The output target_files zip, which should be already open and
673 writable.
674 key_path: The path to the PEM encoded X.509 certificate.
675 """
676 in_cmdline = input_zip.read("BOOT/cmdline")
677 # Copy in_cmdline to output_zip if veritykeyid is not present.
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700678 if "veritykeyid" not in in_cmdline:
Tao Baoe838d142017-12-23 23:44:48 -0800679 common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline)
680 return
681
Tao Bao0c28d2d2017-12-24 10:37:38 -0800682 out_buffer = []
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700683 for param in in_cmdline.split():
Tao Baoe838d142017-12-23 23:44:48 -0800684 if "veritykeyid" not in param:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800685 out_buffer.append(param)
Tao Baoe838d142017-12-23 23:44:48 -0800686 continue
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700687
Tao Baoe838d142017-12-23 23:44:48 -0800688 # Extract keyid using openssl command.
689 p = common.Run(["openssl", "x509", "-in", key_path, "-text"],
Tao Baode1d4792018-02-20 10:05:46 -0800690 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Tao Baoe838d142017-12-23 23:44:48 -0800691 keyid, stderr = p.communicate()
692 assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr)
693 keyid = re.search(
694 r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower()
695 print("Replacing verity keyid with {}".format(keyid))
696 out_buffer.append("veritykeyid=id:%s" % (keyid,))
697
698 out_cmdline = ' '.join(out_buffer).strip() + '\n'
699 common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline)
Tao Bao46a59992017-06-05 11:55:16 -0700700
701
702def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
703 """Replaces META/misc_info.txt.
704
705 Only writes back the ones in the original META/misc_info.txt. Because the
706 current in-memory dict contains additional items computed at runtime.
707 """
708 misc_info_old = common.LoadDictionaryFromLines(
709 input_zip.read('META/misc_info.txt').split('\n'))
710 items = []
711 for key in sorted(misc_info):
712 if key in misc_info_old:
713 items.append('%s=%s' % (key, misc_info[key]))
714 common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700715
Tao Bao8adcfd12016-06-17 17:01:22 -0700716
Tao Bao639118f2017-06-19 15:48:02 -0700717def ReplaceAvbSigningKeys(misc_info):
718 """Replaces the AVB signing keys."""
719
720 AVB_FOOTER_ARGS_BY_PARTITION = {
Tao Bao0c28d2d2017-12-24 10:37:38 -0800721 'boot' : 'avb_boot_add_hash_footer_args',
722 'dtbo' : 'avb_dtbo_add_hash_footer_args',
723 'recovery' : 'avb_recovery_add_hash_footer_args',
724 'system' : 'avb_system_add_hashtree_footer_args',
725 'vendor' : 'avb_vendor_add_hashtree_footer_args',
726 'vbmeta' : 'avb_vbmeta_args',
Tao Bao639118f2017-06-19 15:48:02 -0700727 }
728
729 def ReplaceAvbPartitionSigningKey(partition):
730 key = OPTIONS.avb_keys.get(partition)
731 if not key:
732 return
733
734 algorithm = OPTIONS.avb_algorithms.get(partition)
735 assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
736
Tao Bao0c28d2d2017-12-24 10:37:38 -0800737 print('Replacing AVB signing key for %s with "%s" (%s)' % (
738 partition, key, algorithm))
Tao Bao639118f2017-06-19 15:48:02 -0700739 misc_info['avb_' + partition + '_algorithm'] = algorithm
740 misc_info['avb_' + partition + '_key_path'] = key
741
742 extra_args = OPTIONS.avb_extra_args.get(partition)
743 if extra_args:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800744 print('Setting extra AVB signing args for %s to "%s"' % (
745 partition, extra_args))
Tao Bao639118f2017-06-19 15:48:02 -0700746 args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition]
747 misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
748
749 for partition in AVB_FOOTER_ARGS_BY_PARTITION:
750 ReplaceAvbPartitionSigningKey(partition)
751
752
Doug Zongker831840e2011-09-22 10:28:04 -0700753def BuildKeyMap(misc_info, key_mapping_options):
754 for s, d in key_mapping_options:
755 if s is None: # -d option
756 devkey = misc_info.get("default_system_dev_certificate",
757 "build/target/product/security/testkey")
758 devkeydir = os.path.dirname(devkey)
759
760 OPTIONS.key_map.update({
761 devkeydir + "/testkey": d + "/releasekey",
762 devkeydir + "/devkey": d + "/releasekey",
763 devkeydir + "/media": d + "/media",
764 devkeydir + "/shared": d + "/shared",
765 devkeydir + "/platform": d + "/platform",
766 })
767 else:
768 OPTIONS.key_map[s] = d
769
770
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800771def GetApiLevelAndCodename(input_tf_zip):
772 data = input_tf_zip.read("SYSTEM/build.prop")
773 api_level = None
774 codename = None
775 for line in data.split("\n"):
776 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800777 if line and line[0] != '#' and "=" in line:
778 key, value = line.split("=", 1)
779 key = key.strip()
780 if key == "ro.build.version.sdk":
781 api_level = int(value.strip())
782 elif key == "ro.build.version.codename":
783 codename = value.strip()
784
785 if api_level is None:
786 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
787 if codename is None:
788 raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
789
790 return (api_level, codename)
791
792
793def GetCodenameToApiLevelMap(input_tf_zip):
794 data = input_tf_zip.read("SYSTEM/build.prop")
795 api_level = None
796 codenames = None
797 for line in data.split("\n"):
798 line = line.strip()
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800799 if line and line[0] != '#' and "=" in line:
800 key, value = line.split("=", 1)
801 key = key.strip()
802 if key == "ro.build.version.sdk":
803 api_level = int(value.strip())
804 elif key == "ro.build.version.all_codenames":
805 codenames = value.strip().split(",")
806
807 if api_level is None:
808 raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
809 if codenames is None:
810 raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
811
812 result = dict()
813 for codename in codenames:
814 codename = codename.strip()
815 if len(codename) > 0:
816 result[codename] = api_level
817 return result
818
819
Doug Zongkereef39442009-04-02 12:14:19 -0700820def main(argv):
821
Doug Zongker831840e2011-09-22 10:28:04 -0700822 key_mapping_options = []
823
Doug Zongkereef39442009-04-02 12:14:19 -0700824 def option_handler(o, a):
Doug Zongker05d3dea2009-06-22 11:32:31 -0700825 if o in ("-e", "--extra_apks"):
Doug Zongkereef39442009-04-02 12:14:19 -0700826 names, key = a.split("=")
827 names = names.split(",")
828 for n in names:
829 OPTIONS.extra_apks[n] = key
Tao Bao93c2a012018-06-19 12:19:35 -0700830 elif o == "--skip_apks_with_path_prefix":
831 # Sanity check the prefix, which must be in all upper case.
832 prefix = a.split('/')[0]
833 if not prefix or prefix != prefix.upper():
834 raise ValueError("Invalid path prefix '%s'" % (a,))
835 OPTIONS.skip_apks_with_path_prefix.add(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700836 elif o in ("-d", "--default_key_mappings"):
Doug Zongker831840e2011-09-22 10:28:04 -0700837 key_mapping_options.append((None, a))
Doug Zongkereef39442009-04-02 12:14:19 -0700838 elif o in ("-k", "--key_mapping"):
Doug Zongker831840e2011-09-22 10:28:04 -0700839 key_mapping_options.append(a.split("=", 1))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700840 elif o in ("-o", "--replace_ota_keys"):
841 OPTIONS.replace_ota_keys = True
Doug Zongkerae877012009-04-21 10:04:51 -0700842 elif o in ("-t", "--tag_changes"):
843 new = []
844 for i in a.split(","):
845 i = i.strip()
846 if not i or i[0] not in "-+":
847 raise ValueError("Bad tag change '%s'" % (i,))
848 new.append(i[0] + i[1:].strip())
849 OPTIONS.tag_changes = tuple(new)
Geremy Condraf19b3652014-07-29 17:54:54 -0700850 elif o == "--replace_verity_public_key":
851 OPTIONS.replace_verity_public_key = (True, a)
852 elif o == "--replace_verity_private_key":
853 OPTIONS.replace_verity_private_key = (True, a)
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700854 elif o == "--replace_verity_keyid":
855 OPTIONS.replace_verity_keyid = (True, a)
Tao Bao639118f2017-06-19 15:48:02 -0700856 elif o == "--avb_vbmeta_key":
857 OPTIONS.avb_keys['vbmeta'] = a
858 elif o == "--avb_vbmeta_algorithm":
859 OPTIONS.avb_algorithms['vbmeta'] = a
860 elif o == "--avb_vbmeta_extra_args":
861 OPTIONS.avb_extra_args['vbmeta'] = a
862 elif o == "--avb_boot_key":
863 OPTIONS.avb_keys['boot'] = a
864 elif o == "--avb_boot_algorithm":
865 OPTIONS.avb_algorithms['boot'] = a
866 elif o == "--avb_boot_extra_args":
867 OPTIONS.avb_extra_args['boot'] = a
868 elif o == "--avb_dtbo_key":
869 OPTIONS.avb_keys['dtbo'] = a
870 elif o == "--avb_dtbo_algorithm":
871 OPTIONS.avb_algorithms['dtbo'] = a
872 elif o == "--avb_dtbo_extra_args":
873 OPTIONS.avb_extra_args['dtbo'] = a
874 elif o == "--avb_system_key":
875 OPTIONS.avb_keys['system'] = a
876 elif o == "--avb_system_algorithm":
877 OPTIONS.avb_algorithms['system'] = a
878 elif o == "--avb_system_extra_args":
879 OPTIONS.avb_extra_args['system'] = a
880 elif o == "--avb_vendor_key":
881 OPTIONS.avb_keys['vendor'] = a
882 elif o == "--avb_vendor_algorithm":
883 OPTIONS.avb_algorithms['vendor'] = a
884 elif o == "--avb_vendor_extra_args":
885 OPTIONS.avb_extra_args['vendor'] = a
Doug Zongkereef39442009-04-02 12:14:19 -0700886 else:
887 return False
888 return True
889
Tao Bao639118f2017-06-19 15:48:02 -0700890 args = common.ParseOptions(
891 argv, __doc__,
892 extra_opts="e:d:k:ot:",
893 extra_long_opts=[
Tao Bao0c28d2d2017-12-24 10:37:38 -0800894 "extra_apks=",
Tao Bao93c2a012018-06-19 12:19:35 -0700895 "skip_apks_with_path_prefix=",
Tao Bao0c28d2d2017-12-24 10:37:38 -0800896 "default_key_mappings=",
897 "key_mapping=",
898 "replace_ota_keys",
899 "tag_changes=",
900 "replace_verity_public_key=",
901 "replace_verity_private_key=",
902 "replace_verity_keyid=",
903 "avb_vbmeta_algorithm=",
904 "avb_vbmeta_key=",
905 "avb_vbmeta_extra_args=",
906 "avb_boot_algorithm=",
907 "avb_boot_key=",
908 "avb_boot_extra_args=",
909 "avb_dtbo_algorithm=",
910 "avb_dtbo_key=",
911 "avb_dtbo_extra_args=",
912 "avb_system_algorithm=",
913 "avb_system_key=",
914 "avb_system_extra_args=",
915 "avb_vendor_algorithm=",
916 "avb_vendor_key=",
917 "avb_vendor_extra_args=",
Tao Bao639118f2017-06-19 15:48:02 -0700918 ],
919 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -0700920
921 if len(args) != 2:
922 common.Usage(__doc__)
923 sys.exit(1)
924
925 input_zip = zipfile.ZipFile(args[0], "r")
Tao Bao2b8f4892017-06-13 12:54:58 -0700926 output_zip = zipfile.ZipFile(args[1], "w",
927 compression=zipfile.ZIP_DEFLATED,
928 allowZip64=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700929
Doug Zongker831840e2011-09-22 10:28:04 -0700930 misc_info = common.LoadInfoDict(input_zip)
931
932 BuildKeyMap(misc_info, key_mapping_options)
933
Narayan Kamatha07bf042017-08-14 14:49:21 +0100934 certmap, compressed_extension = common.ReadApkCerts(input_zip)
935 apk_key_map = GetApkCerts(certmap)
936 CheckAllApksSigned(input_zip, apk_key_map, compressed_extension)
Doug Zongkereb338ef2009-05-20 16:50:49 -0700937
938 key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
Tao Bao9aa4b9b2016-09-29 17:53:56 -0700939 platform_api_level, _ = GetApiLevelAndCodename(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800940 codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800941
Doug Zongker412c02f2014-02-13 10:58:24 -0800942 ProcessTargetFiles(input_zip, output_zip, misc_info,
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800943 apk_key_map, key_passwords,
944 platform_api_level,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100945 codename_to_api_level_map,
946 compressed_extension)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700947
Tao Bao2ed665a2015-04-01 11:21:55 -0700948 common.ZipClose(input_zip)
949 common.ZipClose(output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700950
Tianjie Xub48589a2016-08-03 19:21:52 -0700951 # Skip building userdata.img and cache.img when signing the target files.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700952 new_args = ["--is_signing"]
953 # add_img_to_target_files builds the system image from scratch, so the
954 # recovery patch is guaranteed to be regenerated there.
955 if OPTIONS.rebuild_recovery:
956 new_args.append("--rebuild_recovery")
957 new_args.append(args[1])
Tianjie Xub48589a2016-08-03 19:21:52 -0700958 add_img_to_target_files.main(new_args)
Doug Zongker3c84f562014-07-31 11:06:30 -0700959
Tao Bao0c28d2d2017-12-24 10:37:38 -0800960 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -0700961
962
963if __name__ == '__main__':
964 try:
965 main(sys.argv[1:])
Tao Bao0c28d2d2017-12-24 10:37:38 -0800966 except common.ExternalError as e:
967 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -0700968 sys.exit(1)
Tao Bao639118f2017-06-19 15:48:02 -0700969 finally:
970 common.Cleanup()