blob: 393c33d4d3de61cafb289dfad4d6848534e9f46f [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Signs all the APK files in a target-files zipfile, producing a new
19target-files zip.
20
21Usage: sign_target_files_apks [flags] input_target_files output_target_files
22
Doug Zongkereef39442009-04-02 12:14:19 -070023 -e (--extra_apks) <name,name,...=key>
24 Add extra APK name/key pairs as though they appeared in
Doug Zongkerad88c7c2009-04-14 12:34:27 -070025 apkcerts.txt (so mappings specified by -k and -d are applied).
26 Keys specified in -e override any value for that app contained
27 in the apkcerts.txt file. Option may be repeated to give
28 multiple extra packages.
Doug Zongkereef39442009-04-02 12:14:19 -070029
Tao Bao93c2a012018-06-19 12:19:35 -070030 --skip_apks_with_path_prefix <prefix>
31 Skip signing an APK if it has the matching prefix in its path. The prefix
32 should be matching the entry name, which has partition names in upper
33 case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be
34 repeated to give multiple prefixes.
35
Doug Zongkereef39442009-04-02 12:14:19 -070036 -k (--key_mapping) <src_key=dest_key>
37 Add a mapping from the key name as specified in apkcerts.txt (the
38 src_key) to the real key you wish to sign the package with
39 (dest_key). Option may be repeated to give multiple key
40 mappings.
41
42 -d (--default_key_mappings) <dir>
43 Set up the following key mappings:
44
Doug Zongker831840e2011-09-22 10:28:04 -070045 $devkey/devkey ==> $dir/releasekey
46 $devkey/testkey ==> $dir/releasekey
47 $devkey/media ==> $dir/media
48 $devkey/shared ==> $dir/shared
49 $devkey/platform ==> $dir/platform
50
51 where $devkey is the directory part of the value of
52 default_system_dev_certificate from the input target-files's
53 META/misc_info.txt. (Defaulting to "build/target/product/security"
54 if the value is not present in misc_info.
Doug Zongkereef39442009-04-02 12:14:19 -070055
56 -d and -k options are added to the set of mappings in the order
57 in which they appear on the command line.
Doug Zongker8e931bf2009-04-06 15:21:45 -070058
59 -o (--replace_ota_keys)
Tao Baoa80ed222016-06-16 14:41:24 -070060 Replace the certificate (public key) used by OTA package verification
61 with the ones specified in the input target_files zip (in the
62 META/otakeys.txt file). Key remapping (-k and -d) is performed on the
63 keys. For A/B devices, the payload verification key will be replaced
64 as well. If there're multiple OTA keys, only the first one will be used
65 for payload verification.
Doug Zongker17aa9442009-04-17 10:15:58 -070066
Doug Zongkerae877012009-04-21 10:04:51 -070067 -t (--tag_changes) <+tag>,<-tag>,...
68 Comma-separated list of changes to make to the set of tags (in
69 the last component of the build fingerprint). Prefix each with
70 '+' or '-' to indicate whether that tag should be added or
71 removed. Changes are processed in the order they appear.
Doug Zongker831840e2011-09-22 10:28:04 -070072 Default value is "-test-keys,-dev-keys,+release-keys".
Doug Zongkerae877012009-04-21 10:04:51 -070073
Tao Bao8adcfd12016-06-17 17:01:22 -070074 --replace_verity_private_key <key>
75 Replace the private key used for verity signing. It expects a filename
76 WITHOUT the extension (e.g. verity_key).
77
78 --replace_verity_public_key <key>
79 Replace the certificate (public key) used for verity verification. The
80 key file replaces the one at BOOT/RAMDISK/verity_key (or ROOT/verity_key
81 for devices using system_root_image). It expects the key filename WITH
82 the extension (e.g. verity_key.pub).
83
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -070084 --replace_verity_keyid <path_to_X509_PEM_cert_file>
85 Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
Tao Bao8adcfd12016-06-17 17:01:22 -070086 with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
Tao Bao639118f2017-06-19 15:48:02 -070087
88 --avb_{boot,system,vendor,dtbo,vbmeta}_algorithm <algorithm>
89 --avb_{boot,system,vendor,dtbo,vbmeta}_key <key>
90 Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
91 the specified image. Otherwise it uses the existing values in info dict.
92
93 --avb_{boot,system,vendor,dtbo,vbmeta}_extra_args <args>
94 Specify any additional args that are needed to AVB-sign the image
95 (e.g. "--signing_helper /path/to/helper"). The args will be appended to
96 the existing ones in info dict.
Doug Zongkereef39442009-04-02 12:14:19 -070097"""
98
Tao Bao0c28d2d2017-12-24 10:37:38 -080099from __future__ import print_function
Doug Zongkereef39442009-04-02 12:14:19 -0700100
Robert Craig817c5742013-04-19 10:59:22 -0400101import base64
Doug Zongker8e931bf2009-04-06 15:21:45 -0700102import copy
Robert Craig817c5742013-04-19 10:59:22 -0400103import errno
Narayan Kamatha07bf042017-08-14 14:49:21 +0100104import gzip
Doug Zongkereef39442009-04-02 12:14:19 -0700105import os
106import re
Narayan Kamatha07bf042017-08-14 14:49:21 +0100107import shutil
Tao Bao9fdd00f2017-07-12 11:57:05 -0700108import stat
Doug Zongkereef39442009-04-02 12:14:19 -0700109import subprocess
Tao Bao0c28d2d2017-12-24 10:37:38 -0800110import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700111import tempfile
112import zipfile
Tao Bao66472632017-12-04 17:16:36 -0800113from xml.etree import ElementTree
Doug Zongkereef39442009-04-02 12:14:19 -0700114
Doug Zongker3c84f562014-07-31 11:06:30 -0700115import add_img_to_target_files
Doug Zongkereef39442009-04-02 12:14:19 -0700116import common
117
Tao Bao0c28d2d2017-12-24 10:37:38 -0800118
119if sys.hexversion < 0x02070000:
120 print("Python 2.7 or newer is required.", file=sys.stderr)
121 sys.exit(1)
122
123
Doug Zongkereef39442009-04-02 12:14:19 -0700124OPTIONS = common.OPTIONS
125
126OPTIONS.extra_apks = {}
Tao Bao93c2a012018-06-19 12:19:35 -0700127OPTIONS.skip_apks_with_path_prefix = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700128OPTIONS.key_map = {}
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700129OPTIONS.rebuild_recovery = False
Doug Zongker8e931bf2009-04-06 15:21:45 -0700130OPTIONS.replace_ota_keys = False
Geremy Condraf19b3652014-07-29 17:54:54 -0700131OPTIONS.replace_verity_public_key = False
132OPTIONS.replace_verity_private_key = False
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700133OPTIONS.replace_verity_keyid = False
Doug Zongker831840e2011-09-22 10:28:04 -0700134OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
Tao Bao639118f2017-06-19 15:48:02 -0700135OPTIONS.avb_keys = {}
136OPTIONS.avb_algorithms = {}
137OPTIONS.avb_extra_args = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700138
Tao Bao0c28d2d2017-12-24 10:37:38 -0800139
Narayan Kamatha07bf042017-08-14 14:49:21 +0100140def GetApkCerts(certmap):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800141 # apply the key remapping to the contents of the file
142 for apk, cert in certmap.iteritems():
143 certmap[apk] = OPTIONS.key_map.get(cert, cert)
144
145 # apply all the -e options, overriding anything in the file
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700146 for apk, cert in OPTIONS.extra_apks.iteritems():
Doug Zongkerdecf9952009-12-15 17:27:49 -0800147 if not cert:
148 cert = "PRESIGNED"
Doug Zongkerad88c7c2009-04-14 12:34:27 -0700149 certmap[apk] = OPTIONS.key_map.get(cert, cert)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800150
Doug Zongkereef39442009-04-02 12:14:19 -0700151 return certmap
152
153
Tao Bao93c2a012018-06-19 12:19:35 -0700154def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
Tao Bao11f955c2018-06-19 12:19:35 -0700155 """Returns the APK info based on the given filename.
156
157 Checks if the given filename (with path) looks like an APK file, by taking the
Tao Bao93c2a012018-06-19 12:19:35 -0700158 compressed extension into consideration. If it appears to be an APK file,
159 further checks if the APK file should be skipped when signing, based on the
160 given path prefixes.
Tao Bao11f955c2018-06-19 12:19:35 -0700161
162 Args:
163 filename: Path to the file.
164 compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
165 or None if there's no compressed APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700166 skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700167
168 Returns:
Tao Bao93c2a012018-06-19 12:19:35 -0700169 (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
170 given filename is an APK file. is_compressed indicates whether the APK file
171 is compressed (only meaningful when is_apk is True). should_be_skipped
172 indicates whether the filename matches any of the given prefixes to be
173 skipped.
Tao Bao11f955c2018-06-19 12:19:35 -0700174
175 Raises:
Tao Bao93c2a012018-06-19 12:19:35 -0700176 AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
Tao Bao11f955c2018-06-19 12:19:35 -0700177 """
178 assert compressed_extension is None or compressed_extension.startswith('.'), \
179 "Invalid compressed_extension arg: '{}'".format(compressed_extension)
180
Tao Bao93c2a012018-06-19 12:19:35 -0700181 # skipped_prefixes should be one of set/list/tuple types. Other types such as
182 # str shouldn't be accepted.
183 assert (isinstance(skipped_prefixes, tuple) or
184 isinstance(skipped_prefixes, set) or
185 isinstance(skipped_prefixes, list)), \
186 "Invalid skipped_prefixes input type: {}".format(
187 type(skipped_prefixes))
188
Tao Bao11f955c2018-06-19 12:19:35 -0700189 compressed_apk_extension = (
190 ".apk" + compressed_extension if compressed_extension else None)
191 is_apk = (filename.endswith(".apk") or
192 (compressed_apk_extension and
193 filename.endswith(compressed_apk_extension)))
194 if not is_apk:
Tao Bao93c2a012018-06-19 12:19:35 -0700195 return (False, False, False)
Tao Bao11f955c2018-06-19 12:19:35 -0700196
197 is_compressed = (compressed_apk_extension and
198 filename.endswith(compressed_apk_extension))
Tao Bao93c2a012018-06-19 12:19:35 -0700199 should_be_skipped = filename.startswith(tuple(skipped_prefixes))
200 return (True, is_compressed, should_be_skipped)
Tao Bao11f955c2018-06-19 12:19:35 -0700201
202
Narayan Kamatha07bf042017-08-14 14:49:21 +0100203def CheckAllApksSigned(input_tf_zip, apk_key_map, compressed_extension):
Tao Bao11f955c2018-06-19 12:19:35 -0700204 """Checks that all the APKs have keys specified, otherwise errors out.
205
206 Args:
207 input_tf_zip: An open target_files zip file.
208 apk_key_map: A dict of known signing keys key'd by APK names.
209 compressed_extension: The extension string of compressed APKs, such as
210 ".gz", or None if there's no compressed APKs.
211
212 Raises:
213 AssertionError: On finding unknown APKs.
214 """
Doug Zongkereb338ef2009-05-20 16:50:49 -0700215 unknown_apks = []
216 for info in input_tf_zip.infolist():
Tao Bao93c2a012018-06-19 12:19:35 -0700217 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
218 info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
219 if not is_apk or should_be_skipped:
Tao Bao11f955c2018-06-19 12:19:35 -0700220 continue
221 name = os.path.basename(info.filename)
222 if is_compressed:
223 name = name[:-len(compressed_extension)]
224 if name not in apk_key_map:
225 unknown_apks.append(name)
226
227 assert not unknown_apks, \
228 ("No key specified for:\n {}\n"
229 "Use '-e <apkname>=' to specify a key (which may be an empty string to "
230 "not sign this apk).".format("\n ".join(unknown_apks)))
Doug Zongkereb338ef2009-05-20 16:50:49 -0700231
232
Narayan Kamatha07bf042017-08-14 14:49:21 +0100233def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
234 is_compressed):
Doug Zongkereef39442009-04-02 12:14:19 -0700235 unsigned = tempfile.NamedTemporaryFile()
236 unsigned.write(data)
237 unsigned.flush()
238
Narayan Kamatha07bf042017-08-14 14:49:21 +0100239 if is_compressed:
240 uncompressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800241 with gzip.open(unsigned.name, "rb") as in_file, \
242 open(uncompressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100243 shutil.copyfileobj(in_file, out_file)
244
245 # Finally, close the "unsigned" file (which is gzip compressed), and then
246 # replace it with the uncompressed version.
247 #
248 # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
249 # we could just gzip / gunzip in-memory buffers instead.
250 unsigned.close()
251 unsigned = uncompressed
252
Doug Zongkereef39442009-04-02 12:14:19 -0700253 signed = tempfile.NamedTemporaryFile()
254
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800255 # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
256 # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
257 # didn't change, we don't want its signature to change due to the switch
258 # from SHA-1 to SHA-256.
259 # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
260 # is 18 or higher. For pre-N builds we disable this mechanism by pretending
261 # that the APK's minSdkVersion is 1.
262 # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
263 # determine whether to use SHA-256.
264 min_api_level = None
265 if platform_api_level > 23:
266 # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
267 # minSdkVersion attribute
268 min_api_level = None
269 else:
270 # Force APK signer to use SHA-1
271 min_api_level = 1
272
273 common.SignFile(unsigned.name, signed.name, keyname, pw,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800274 min_api_level=min_api_level,
275 codename_to_api_level_map=codename_to_api_level_map)
Doug Zongkereef39442009-04-02 12:14:19 -0700276
Tao Bao0c28d2d2017-12-24 10:37:38 -0800277 data = None
Narayan Kamatha07bf042017-08-14 14:49:21 +0100278 if is_compressed:
279 # Recompress the file after it has been signed.
280 compressed = tempfile.NamedTemporaryFile()
Tao Bao0c28d2d2017-12-24 10:37:38 -0800281 with open(signed.name, "rb") as in_file, \
282 gzip.open(compressed.name, "wb") as out_file:
Narayan Kamatha07bf042017-08-14 14:49:21 +0100283 shutil.copyfileobj(in_file, out_file)
284
285 data = compressed.read()
286 compressed.close()
287 else:
288 data = signed.read()
289
Doug Zongkereef39442009-04-02 12:14:19 -0700290 unsigned.close()
291 signed.close()
292
293 return data
294
295
Doug Zongker412c02f2014-02-13 10:58:24 -0800296def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800297 apk_key_map, key_passwords, platform_api_level,
Narayan Kamatha07bf042017-08-14 14:49:21 +0100298 codename_to_api_level_map,
299 compressed_extension):
Tao Bao93c2a012018-06-19 12:19:35 -0700300 # maxsize measures the maximum filename length, including the ones to be
301 # skipped.
Tao Bao0c28d2d2017-12-24 10:37:38 -0800302 maxsize = max(
303 [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
Tao Bao93c2a012018-06-19 12:19:35 -0700304 if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
Tao Baoa80ed222016-06-16 14:41:24 -0700305 system_root_image = misc_info.get("system_root_image") == "true"
Doug Zongker412c02f2014-02-13 10:58:24 -0800306
Doug Zongkereef39442009-04-02 12:14:19 -0700307 for info in input_tf_zip.infolist():
Tao Bao11f955c2018-06-19 12:19:35 -0700308 filename = info.filename
309 if filename.startswith("IMAGES/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700310 continue
Doug Zongker3c84f562014-07-31 11:06:30 -0700311
Tao Bao11f955c2018-06-19 12:19:35 -0700312 data = input_tf_zip.read(filename)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700313 out_info = copy.copy(info)
Tao Bao93c2a012018-06-19 12:19:35 -0700314 (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
315 filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
316
317 if is_apk and should_be_skipped:
318 # Copy skipped APKs verbatim.
319 print(
320 "NOT signing: %s\n"
321 " (skipped due to matching prefix)" % (filename,))
322 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker412c02f2014-02-13 10:58:24 -0800323
Tao Baof2cffbd2015-07-22 12:33:18 -0700324 # Sign APKs.
Tao Bao93c2a012018-06-19 12:19:35 -0700325 elif is_apk:
Tao Bao11f955c2018-06-19 12:19:35 -0700326 name = os.path.basename(filename)
Narayan Kamatha07bf042017-08-14 14:49:21 +0100327 if is_compressed:
328 name = name[:-len(compressed_extension)]
329
Doug Zongker43874f82009-04-14 14:05:15 -0700330 key = apk_key_map[name]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800331 if key not in common.SPECIAL_CERT_STRINGS:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800332 print(" signing: %-*s (%s)" % (maxsize, name, key))
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800333 signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
Tao Bao0c28d2d2017-12-24 10:37:38 -0800334 codename_to_api_level_map, is_compressed)
Tao Bao2ed665a2015-04-01 11:21:55 -0700335 common.ZipWriteStr(output_tf_zip, out_info, signed_data)
Doug Zongkereef39442009-04-02 12:14:19 -0700336 else:
337 # an APK we're not supposed to sign.
Tao Bao93c2a012018-06-19 12:19:35 -0700338 print(
339 "NOT signing: %s\n"
340 " (skipped due to special cert string)" % (name,))
Tao Bao2ed665a2015-04-01 11:21:55 -0700341 common.ZipWriteStr(output_tf_zip, out_info, data)
Tao Baoa80ed222016-06-16 14:41:24 -0700342
343 # System properties.
Tao Bao11f955c2018-06-19 12:19:35 -0700344 elif filename in ("SYSTEM/build.prop",
345 "VENDOR/build.prop",
346 "SYSTEM/etc/prop.default",
347 "BOOT/RAMDISK/prop.default",
348 "BOOT/RAMDISK/default.prop", # legacy
349 "ROOT/default.prop", # legacy
350 "RECOVERY/RAMDISK/prop.default",
351 "RECOVERY/RAMDISK/default.prop"): # legacy
352 print("Rewriting %s:" % (filename,))
Hung-ying Tyan7eb6a922017-05-01 21:56:26 +0800353 if stat.S_ISLNK(info.external_attr >> 16):
354 new_data = data
355 else:
Tao Baoa7054ee2017-12-08 14:42:16 -0800356 new_data = RewriteProps(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700357 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700358
Tao Bao66472632017-12-04 17:16:36 -0800359 # Replace the certs in *mac_permissions.xml (there could be multiple, such
360 # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
Tao Bao11f955c2018-06-19 12:19:35 -0700361 elif filename.endswith("mac_permissions.xml"):
362 print("Rewriting %s with new keys." % (filename,))
Robert Craig817c5742013-04-19 10:59:22 -0400363 new_data = ReplaceCerts(data)
Tao Bao2ed665a2015-04-01 11:21:55 -0700364 common.ZipWriteStr(output_tf_zip, out_info, new_data)
Tao Baoa80ed222016-06-16 14:41:24 -0700365
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700366 # Ask add_img_to_target_files to rebuild the recovery patch if needed.
Tao Bao11f955c2018-06-19 12:19:35 -0700367 elif filename in ("SYSTEM/recovery-from-boot.p",
368 "SYSTEM/etc/recovery.img",
369 "SYSTEM/bin/install-recovery.sh"):
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700370 OPTIONS.rebuild_recovery = True
Tao Baoa80ed222016-06-16 14:41:24 -0700371
372 # Don't copy OTA keys if we're replacing them.
Doug Zongker412c02f2014-02-13 10:58:24 -0800373 elif (OPTIONS.replace_ota_keys and
Tao Bao11f955c2018-06-19 12:19:35 -0700374 filename in (
Tao Baoa80ed222016-06-16 14:41:24 -0700375 "BOOT/RAMDISK/res/keys",
Alex Deymob3e8ce62016-08-04 16:06:12 -0700376 "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem",
Tao Baoa80ed222016-06-16 14:41:24 -0700377 "RECOVERY/RAMDISK/res/keys",
378 "SYSTEM/etc/security/otacerts.zip",
379 "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
Doug Zongker412c02f2014-02-13 10:58:24 -0800380 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700381
Tao Bao46a59992017-06-05 11:55:16 -0700382 # Skip META/misc_info.txt since we will write back the new values later.
Tao Bao11f955c2018-06-19 12:19:35 -0700383 elif filename == "META/misc_info.txt":
Geremy Condraf19b3652014-07-29 17:54:54 -0700384 pass
Tao Bao8adcfd12016-06-17 17:01:22 -0700385
386 # Skip verity public key if we will replace it.
Michael Runge947894f2014-10-14 20:58:38 -0700387 elif (OPTIONS.replace_verity_public_key and
Tao Bao11f955c2018-06-19 12:19:35 -0700388 filename in ("BOOT/RAMDISK/verity_key",
389 "ROOT/verity_key")):
Geremy Condraf19b3652014-07-29 17:54:54 -0700390 pass
Tao Baoa80ed222016-06-16 14:41:24 -0700391
Tao Bao8adcfd12016-06-17 17:01:22 -0700392 # Skip verity keyid (for system_root_image use) if we will replace it.
Tao Bao11f955c2018-06-19 12:19:35 -0700393 elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
Badhri Jagan Sridharan35c9b122016-06-16 19:58:44 -0700394 pass
395
Tianjie Xu4f099002016-08-11 18:04:27 -0700396 # Skip the care_map as we will regenerate the system/vendor images.
Tao Bao11f955c2018-06-19 12:19:35 -0700397 elif filename == "META/care_map.txt":
Tianjie Xu4f099002016-08-11 18:04:27 -0700398 pass
399
Tao Baoa80ed222016-06-16 14:41:24 -0700400 # A non-APK file; copy it verbatim.
Doug Zongkereef39442009-04-02 12:14:19 -0700401 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700402 common.ZipWriteStr(output_tf_zip, out_info, data)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700403
Doug Zongker412c02f2014-02-13 10:58:24 -0800404 if OPTIONS.replace_ota_keys:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700405 ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
Doug Zongker412c02f2014-02-13 10:58:24 -0800406
Tao Bao46a59992017-06-05 11:55:16 -0700407 # Replace the keyid string in misc_info dict.
Tao Bao8adcfd12016-06-17 17:01:22 -0700408 if OPTIONS.replace_verity_private_key:
Tao Bao46a59992017-06-05 11:55:16 -0700409 ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700410
411 if OPTIONS.replace_verity_public_key:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800412 dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
Tao Bao8adcfd12016-06-17 17:01:22 -0700413 # We are replacing the one in boot image only, since the one under
414 # recovery won't ever be needed.
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700415 ReplaceVerityPublicKey(
Tao Bao8adcfd12016-06-17 17:01:22 -0700416 output_tf_zip, dest, OPTIONS.replace_verity_public_key[1])
Tao Bao8adcfd12016-06-17 17:01:22 -0700417
418 # Replace the keyid string in BOOT/cmdline.
419 if OPTIONS.replace_verity_keyid:
Tianjie Xu616fbeb2017-05-23 14:51:02 -0700420 ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
421 OPTIONS.replace_verity_keyid[1])
Doug Zongker412c02f2014-02-13 10:58:24 -0800422
Tao Bao639118f2017-06-19 15:48:02 -0700423 # Replace the AVB signing keys, if any.
424 ReplaceAvbSigningKeys(misc_info)
425
Tao Bao46a59992017-06-05 11:55:16 -0700426 # Write back misc_info with the latest values.
427 ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
428
Doug Zongker8e931bf2009-04-06 15:21:45 -0700429
Robert Craig817c5742013-04-19 10:59:22 -0400430def ReplaceCerts(data):
Tao Bao66472632017-12-04 17:16:36 -0800431 """Replaces all the occurences of X.509 certs with the new ones.
Robert Craig817c5742013-04-19 10:59:22 -0400432
Tao Bao66472632017-12-04 17:16:36 -0800433 The mapping info is read from OPTIONS.key_map. Non-existent certificate will
434 be skipped. After the replacement, it additionally checks for duplicate
435 entries, which would otherwise fail the policy loading code in
436 frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
437
438 Args:
439 data: Input string that contains a set of X.509 certs.
440
441 Returns:
442 A string after the replacement.
443
444 Raises:
445 AssertionError: On finding duplicate entries.
446 """
447 for old, new in OPTIONS.key_map.iteritems():
448 if OPTIONS.verbose:
449 print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new))
450
451 try:
452 with open(old + ".x509.pem") as old_fp:
453 old_cert16 = base64.b16encode(
454 common.ParseCertificate(old_fp.read())).lower()
455 with open(new + ".x509.pem") as new_fp:
456 new_cert16 = base64.b16encode(
457 common.ParseCertificate(new_fp.read())).lower()
458 except IOError as e:
459 if OPTIONS.verbose or e.errno != errno.ENOENT:
460 print(" Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
461 "%s.x509.pem." % (e.filename, e.strerror, old, new))
462 continue
463
464 # Only match entire certs.
465 pattern = "\\b" + old_cert16 + "\\b"
466 (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
467
468 if OPTIONS.verbose:
469 print(" Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
470 num, old, new))
471
472 # Verify that there're no duplicate entries after the replacement. Note that
473 # it's only checking entries with global seinfo at the moment (i.e. ignoring
474 # the ones with inner packages). (Bug: 69479366)
475 root = ElementTree.fromstring(data)
476 signatures = [signer.attrib['signature'] for signer in root.findall('signer')]
477 assert len(signatures) == len(set(signatures)), \
478 "Found duplicate entries after cert replacement: {}".format(data)
Robert Craig817c5742013-04-19 10:59:22 -0400479
480 return data
481
482
Doug Zongkerc09abc82010-01-11 13:09:15 -0800483def EditTags(tags):
Tao Baoa7054ee2017-12-08 14:42:16 -0800484 """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
485
486 Args:
487 tags: The input string that contains comma-separated tags.
488
489 Returns:
490 The updated tags (comma-separated and sorted).
491 """
Doug Zongkerc09abc82010-01-11 13:09:15 -0800492 tags = set(tags.split(","))
493 for ch in OPTIONS.tag_changes:
494 if ch[0] == "-":
495 tags.discard(ch[1:])
496 elif ch[0] == "+":
497 tags.add(ch[1:])
498 return ",".join(sorted(tags))
499
500
Tao Baoa7054ee2017-12-08 14:42:16 -0800501def RewriteProps(data):
502 """Rewrites the system properties in the given string.
503
504 Each property is expected in 'key=value' format. The properties that contain
505 build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
506 EditTags().
507
508 Args:
509 data: Input string, separated by newlines.
510
511 Returns:
512 The string with modified properties.
513 """
Doug Zongker17aa9442009-04-17 10:15:58 -0700514 output = []
515 for line in data.split("\n"):
516 line = line.strip()
517 original_line = line
Michael Rungedc2661a2014-06-03 14:43:11 -0700518 if line and line[0] != '#' and "=" in line:
Doug Zongker17aa9442009-04-17 10:15:58 -0700519 key, value = line.split("=", 1)
Tao Baoa7054ee2017-12-08 14:42:16 -0800520 if key in ("ro.build.fingerprint", "ro.build.thumbprint",
521 "ro.vendor.build.fingerprint", "ro.vendor.build.thumbprint"):
Doug Zongkerc09abc82010-01-11 13:09:15 -0800522 pieces = value.split("/")
523 pieces[-1] = EditTags(pieces[-1])
524 value = "/".join(pieces)
Tao Baocb7ff772015-09-11 15:27:56 -0700525 elif key == "ro.bootimage.build.fingerprint":
526 pieces = value.split("/")
527 pieces[-1] = EditTags(pieces[-1])
528 value = "/".join(pieces)
Doug Zongker17aa9442009-04-17 10:15:58 -0700529 elif key == "ro.build.description":
Doug Zongkerc09abc82010-01-11 13:09:15 -0800530 pieces = value.split(" ")
Doug Zongker17aa9442009-04-17 10:15:58 -0700531 assert len(pieces) == 5
Doug Zongkerc09abc82010-01-11 13:09:15 -0800532 pieces[-1] = EditTags(pieces[-1])
533 value = " ".join(pieces)
534 elif key == "ro.build.tags":
535 value = EditTags(value)
Doug Zongkera8608a72013-07-23 11:51:04 -0700536 elif key == "ro.build.display.id":
537 # change, eg, "JWR66N dev-keys" to "JWR66N"
538 value = value.split()
Michael Rungedc2661a2014-06-03 14:43:11 -0700539 if len(value) > 1 and value[-1].endswith("-keys"):
Andrew Boie73d5abb2013-12-11 12:42:03 -0800540 value.pop()
541 value = " ".join(value)
Doug Zongkerc09abc82010-01-11 13:09:15 -0800542 line = key + "=" + value
Doug Zongker17aa9442009-04-17 10:15:58 -0700543 if line != original_line:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800544 print(" replace: ", original_line)
545 print(" with: ", line)
Doug Zongker17aa9442009-04-17 10:15:58 -0700546 output.append(line)
547 return "\n".join(output) + "\n"
548
549
Doug Zongker831840e2011-09-22 10:28:04 -0700550def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
Doug Zongker8e931bf2009-04-06 15:21:45 -0700551 try:
552 keylist = input_tf_zip.read("META/otakeys.txt").split()
553 except KeyError:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700554 raise common.ExternalError("can't read META/otakeys.txt from input")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700555
Tao Baof718f902017-11-09 10:10:10 -0800556 extra_recovery_keys = misc_info.get("extra_recovery_keys")
Doug Zongkere121d6a2011-02-01 14:13:52 -0800557 if extra_recovery_keys:
558 extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
559 for k in extra_recovery_keys.split()]
560 if extra_recovery_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800561 print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
Doug Zongkere121d6a2011-02-01 14:13:52 -0800562 else:
563 extra_recovery_keys = []
564
Doug Zongker8e931bf2009-04-06 15:21:45 -0700565 mapped_keys = []
566 for k in keylist:
567 m = re.match(r"^(.*)\.x509\.pem$", k)
568 if not m:
Doug Zongker412c02f2014-02-13 10:58:24 -0800569 raise common.ExternalError(
570 "can't parse \"%s\" from META/otakeys.txt" % (k,))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700571 k = m.group(1)
572 mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
573
Doug Zongkere05628c2009-08-20 17:38:42 -0700574 if mapped_keys:
Tao Bao0c28d2d2017-12-24 10:37:38 -0800575 print("using:\n ", "\n ".join(mapped_keys))
576 print("for OTA package verification")
Doug Zongkere05628c2009-08-20 17:38:42 -0700577 else:
Doug Zongker831840e2011-09-22 10:28:04 -0700578 devkey = misc_info.get("default_system_dev_certificate",
579 "build/target/product/security/testkey")
Tao Baof718f902017-11-09 10:10:10 -0800580 mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
581 if mapped_devkey != devkey:
582 misc_info["default_system_dev_certificate"] = mapped_devkey
583 mapped_keys.append(mapped_devkey + ".x509.pem")
Tao Baoa80ed222016-06-16 14:41:24 -0700584 print("META/otakeys.txt has no keys; using %s for OTA package"
585 " verification." % (mapped_keys[0],))
Doug Zongker8e931bf2009-04-06 15:21:45 -0700586
587 # recovery uses a version of the key that has been slightly
588 # predigested (by DumpPublicKey.java) and put in res/keys.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800589 # extra_recovery_keys are used only in recovery.
Tao Baoe95540e2016-11-08 12:08:53 -0800590 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
591 ["-jar",
592 os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")] +
593 mapped_keys + extra_recovery_keys)
594 p = common.Run(cmd, stdout=subprocess.PIPE)
Doug Zongker412c02f2014-02-13 10:58:24 -0800595 new_recovery_keys, _ = p.communicate()
Doug Zongker8e931bf2009-04-06 15:21:45 -0700596 if p.returncode != 0:
T.R. Fullharta28acc62013-03-18 10:31:26 -0700597 raise common.ExternalError("failed to run dumpkeys")
Tao Baoa80ed222016-06-16 14:41:24 -0700598
599 # system_root_image puts the recovery keys at BOOT/RAMDISK.
600 if misc_info.get("system_root_image") == "true":
601 recovery_keys_location = "BOOT/RAMDISK/res/keys"
602 else:
603 recovery_keys_location = "RECOVERY/RAMDISK/res/keys"
604 common.ZipWriteStr(output_tf_zip, recovery_keys_location, new_recovery_keys)
Doug Zongker8e931bf2009-04-06 15:21:45 -0700605
606 # SystemUpdateActivity uses the x509.pem version of the keys, but
607 # put into a zipfile system/etc/security/otacerts.zip.
Doug Zongkere121d6a2011-02-01 14:13:52 -0800608 # We DO NOT include the extra_recovery_keys (if any) here.
Doug Zongker8e931bf2009-04-06 15:21:45 -0700609
Tao Bao0c28d2d2017-12-24 10:37:38 -0800610 try:
611 from StringIO import StringIO
612 except ImportError:
613 from io import StringIO
614 temp_file = StringIO()
Dan Albert8b72aef2015-03-23 19:13:21 -0700615 certs_zip = zipfile.ZipFile(temp_file, "w")
Doug Zongker8e931bf2009-04-06 15:21:45 -0700616 for k in mapped_keys:
Tao Bao83cd79d2016-04-11 23:05:52 -0700617 common.ZipWrite(certs_zip, k)
618 common.ZipClose(certs_zip)
Doug Zongker048e7ca2009-06-15 14:31:53 -0700619 common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
Dan Albert8b72aef2015-03-23 19:13:21 -0700620 temp_file.getvalue())
Doug Zongkereef39442009-04-02 12:14:19 -0700621
Tao Baoa80ed222016-06-16 14:41:24 -0700622 # For A/B devices, update the payload verification key.
623 if misc_info.get("ab_update") == "true":
624 # Unlike otacerts.zip that may contain multiple keys, we can only specify
625 # ONE payload verification key.
626 if len(mapped_keys) > 1:
627 print("\n WARNING: Found more than one OTA keys; Using the first one"
628 " as payload verification key.\n\n")
629
Tao Bao0c28d2d2017-12-24 10:37:38 -0800630 print("Using %s for payload verification." % (mapped_keys[0],))
Tao Bao04e1f012018-02-04 12:13:35 -0800631 pubkey = common.ExtractPublicKey(mapped_keys[0])
Tao Bao13b69622016-07-06 15:28:59 -0700632 common.ZipWriteStr(
Tao Baoa80ed222016-06-16 14:41:24 -0700633 output_tf_zip,
Tao Bao13b69622016-07-06 15:28:59 -0700634 "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
635 pubkey)
Alex Deymob3e8ce62016-08-04 16:06:12 -0700636 common.ZipWriteStr(
637 output_tf_zip,
638 "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem",
639 pubkey)
Tao Baoa80ed222016-06-16 14:41:24 -0700640
Doug Zongker412c02f2014-02-13 10:58:24 -0800641 return new_recovery_keys
642
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()