| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1 | #!/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 | """ | 
|  | 18 | Signs all the APK files in a target-files zipfile, producing a new | 
|  | 19 | target-files zip. | 
|  | 20 |  | 
|  | 21 | Usage:  sign_target_files_apks [flags] input_target_files output_target_files | 
|  | 22 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 23 | -e  (--extra_apks)  <name,name,...=key> | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 24 | Add extra APK/APEX name/key pairs as though they appeared in apkcerts.txt | 
|  | 25 | or apexkeys.txt (so mappings specified by -k and -d are applied). Keys | 
|  | 26 | specified in -e override any value for that app contained in the | 
|  | 27 | apkcerts.txt file, or the container key for an APEX. Option may be | 
|  | 28 | repeated to give multiple extra packages. | 
|  | 29 |  | 
|  | 30 | --extra_apex_payload_key <name=key> | 
|  | 31 | Add a mapping for APEX package name to payload signing key, which will | 
|  | 32 | override the default payload signing key in apexkeys.txt. Note that the | 
|  | 33 | container key should be overridden via the `--extra_apks` flag above. | 
|  | 34 | Option may be repeated for multiple APEXes. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 35 |  | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 36 | --skip_apks_with_path_prefix  <prefix> | 
|  | 37 | Skip signing an APK if it has the matching prefix in its path. The prefix | 
|  | 38 | should be matching the entry name, which has partition names in upper | 
|  | 39 | case, e.g. "VENDOR/app/", or "SYSTEM_OTHER/preloads/". Option may be | 
|  | 40 | repeated to give multiple prefixes. | 
|  | 41 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 42 | -k  (--key_mapping)  <src_key=dest_key> | 
|  | 43 | Add a mapping from the key name as specified in apkcerts.txt (the | 
|  | 44 | src_key) to the real key you wish to sign the package with | 
|  | 45 | (dest_key).  Option may be repeated to give multiple key | 
|  | 46 | mappings. | 
|  | 47 |  | 
|  | 48 | -d  (--default_key_mappings)  <dir> | 
|  | 49 | Set up the following key mappings: | 
|  | 50 |  | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 51 | $devkey/devkey    ==>  $dir/releasekey | 
|  | 52 | $devkey/testkey   ==>  $dir/releasekey | 
|  | 53 | $devkey/media     ==>  $dir/media | 
|  | 54 | $devkey/shared    ==>  $dir/shared | 
|  | 55 | $devkey/platform  ==>  $dir/platform | 
|  | 56 |  | 
|  | 57 | where $devkey is the directory part of the value of | 
|  | 58 | default_system_dev_certificate from the input target-files's | 
| Dan Willemsen | 0ab1be6 | 2019-04-09 21:35:37 -0700 | [diff] [blame] | 59 | META/misc_info.txt.  (Defaulting to "build/make/target/product/security" | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 60 | if the value is not present in misc_info. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 61 |  | 
|  | 62 | -d and -k options are added to the set of mappings in the order | 
|  | 63 | in which they appear on the command line. | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 64 |  | 
|  | 65 | -o  (--replace_ota_keys) | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 66 | Replace the certificate (public key) used by OTA package verification | 
|  | 67 | with the ones specified in the input target_files zip (in the | 
|  | 68 | META/otakeys.txt file). Key remapping (-k and -d) is performed on the | 
|  | 69 | keys. For A/B devices, the payload verification key will be replaced | 
|  | 70 | as well. If there're multiple OTA keys, only the first one will be used | 
|  | 71 | for payload verification. | 
| Doug Zongker | 17aa944 | 2009-04-17 10:15:58 -0700 | [diff] [blame] | 72 |  | 
| Doug Zongker | ae87701 | 2009-04-21 10:04:51 -0700 | [diff] [blame] | 73 | -t  (--tag_changes)  <+tag>,<-tag>,... | 
|  | 74 | Comma-separated list of changes to make to the set of tags (in | 
|  | 75 | the last component of the build fingerprint).  Prefix each with | 
|  | 76 | '+' or '-' to indicate whether that tag should be added or | 
|  | 77 | removed.  Changes are processed in the order they appear. | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 78 | Default value is "-test-keys,-dev-keys,+release-keys". | 
| Doug Zongker | ae87701 | 2009-04-21 10:04:51 -0700 | [diff] [blame] | 79 |  | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 80 | --replace_verity_private_key <key> | 
|  | 81 | Replace the private key used for verity signing. It expects a filename | 
|  | 82 | WITHOUT the extension (e.g. verity_key). | 
|  | 83 |  | 
|  | 84 | --replace_verity_public_key <key> | 
|  | 85 | Replace the certificate (public key) used for verity verification. The | 
|  | 86 | key file replaces the one at BOOT/RAMDISK/verity_key (or ROOT/verity_key | 
|  | 87 | for devices using system_root_image). It expects the key filename WITH | 
|  | 88 | the extension (e.g. verity_key.pub). | 
|  | 89 |  | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 90 | --replace_verity_keyid <path_to_X509_PEM_cert_file> | 
|  | 91 | Replace the veritykeyid in BOOT/cmdline of input_target_file_zip | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 92 | with keyid of the cert pointed by <path_to_X509_PEM_cert_file>. | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 93 |  | 
| Tao Bao | d6085d6 | 2019-05-06 12:55:42 -0700 | [diff] [blame] | 94 | --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system, | 
|  | 95 | vbmeta_vendor}_algorithm <algorithm> | 
|  | 96 | --avb_{boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system, | 
|  | 97 | vbmeta_vendor}_key <key> | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 98 | Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign | 
|  | 99 | the specified image. Otherwise it uses the existing values in info dict. | 
|  | 100 |  | 
| Tao Bao | d6085d6 | 2019-05-06 12:55:42 -0700 | [diff] [blame] | 101 | --avb_{apex,boot,system,system_other,vendor,dtbo,vbmeta,vbmeta_system, | 
|  | 102 | vbmeta_vendor}_extra_args <args> | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 103 | Specify any additional args that are needed to AVB-sign the image | 
|  | 104 | (e.g. "--signing_helper /path/to/helper"). The args will be appended to | 
|  | 105 | the existing ones in info dict. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 106 | """ | 
|  | 107 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 108 | from __future__ import print_function | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 109 |  | 
| Robert Craig | 817c574 | 2013-04-19 10:59:22 -0400 | [diff] [blame] | 110 | import base64 | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 111 | import copy | 
| Robert Craig | 817c574 | 2013-04-19 10:59:22 -0400 | [diff] [blame] | 112 | import errno | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 113 | import gzip | 
| Tao Bao | bb73388 | 2019-07-24 23:31:19 -0700 | [diff] [blame] | 114 | import io | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 115 | import itertools | 
| Tao Bao | badceb2 | 2019-03-15 09:33:43 -0700 | [diff] [blame] | 116 | import logging | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 117 | import os | 
|  | 118 | import re | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 119 | import shutil | 
| Tao Bao | 9fdd00f | 2017-07-12 11:57:05 -0700 | [diff] [blame] | 120 | import stat | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 121 | import subprocess | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 122 | import sys | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 123 | import tempfile | 
|  | 124 | import zipfile | 
| Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 125 | from xml.etree import ElementTree | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 126 |  | 
| Doug Zongker | 3c84f56 | 2014-07-31 11:06:30 -0700 | [diff] [blame] | 127 | import add_img_to_target_files | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 128 | import apex_utils | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 129 | import common | 
|  | 130 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 131 |  | 
|  | 132 | if sys.hexversion < 0x02070000: | 
|  | 133 | print("Python 2.7 or newer is required.", file=sys.stderr) | 
|  | 134 | sys.exit(1) | 
|  | 135 |  | 
|  | 136 |  | 
| Tao Bao | badceb2 | 2019-03-15 09:33:43 -0700 | [diff] [blame] | 137 | logger = logging.getLogger(__name__) | 
|  | 138 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 139 | OPTIONS = common.OPTIONS | 
|  | 140 |  | 
|  | 141 | OPTIONS.extra_apks = {} | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 142 | OPTIONS.extra_apex_payload_keys = {} | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 143 | OPTIONS.skip_apks_with_path_prefix = set() | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 144 | OPTIONS.key_map = {} | 
| Tianjie Xu | 616fbeb | 2017-05-23 14:51:02 -0700 | [diff] [blame] | 145 | OPTIONS.rebuild_recovery = False | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 146 | OPTIONS.replace_ota_keys = False | 
| Geremy Condra | f19b365 | 2014-07-29 17:54:54 -0700 | [diff] [blame] | 147 | OPTIONS.replace_verity_public_key = False | 
|  | 148 | OPTIONS.replace_verity_private_key = False | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 149 | OPTIONS.replace_verity_keyid = False | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 150 | OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys") | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 151 | OPTIONS.avb_keys = {} | 
|  | 152 | OPTIONS.avb_algorithms = {} | 
|  | 153 | OPTIONS.avb_extra_args = {} | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 154 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 155 |  | 
| Tao Bao | 19b02fe | 2019-10-09 00:04:28 -0700 | [diff] [blame] | 156 | AVB_FOOTER_ARGS_BY_PARTITION = { | 
|  | 157 | 'boot' : 'avb_boot_add_hash_footer_args', | 
|  | 158 | 'dtbo' : 'avb_dtbo_add_hash_footer_args', | 
|  | 159 | 'recovery' : 'avb_recovery_add_hash_footer_args', | 
|  | 160 | 'system' : 'avb_system_add_hashtree_footer_args', | 
|  | 161 | 'system_other' : 'avb_system_other_add_hashtree_footer_args', | 
|  | 162 | 'vendor' : 'avb_vendor_add_hashtree_footer_args', | 
|  | 163 | 'vendor_boot' : 'avb_vendor_boot_add_hash_footer_args', | 
|  | 164 | 'vbmeta' : 'avb_vbmeta_args', | 
|  | 165 | 'vbmeta_system' : 'avb_vbmeta_system_args', | 
|  | 166 | 'vbmeta_vendor' : 'avb_vbmeta_vendor_args', | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 |  | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 170 | def GetApkCerts(certmap): | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 171 | # apply the key remapping to the contents of the file | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 172 | for apk, cert in certmap.items(): | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 173 | certmap[apk] = OPTIONS.key_map.get(cert, cert) | 
|  | 174 |  | 
|  | 175 | # apply all the -e options, overriding anything in the file | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 176 | for apk, cert in OPTIONS.extra_apks.items(): | 
| Doug Zongker | decf995 | 2009-12-15 17:27:49 -0800 | [diff] [blame] | 177 | if not cert: | 
|  | 178 | cert = "PRESIGNED" | 
| Doug Zongker | ad88c7c | 2009-04-14 12:34:27 -0700 | [diff] [blame] | 179 | certmap[apk] = OPTIONS.key_map.get(cert, cert) | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 180 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 181 | return certmap | 
|  | 182 |  | 
|  | 183 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 184 | def GetApexKeys(keys_info, key_map): | 
|  | 185 | """Gets APEX payload and container signing keys by applying the mapping rules. | 
|  | 186 |  | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 187 | Presigned payload / container keys will be set accordingly. | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 188 |  | 
|  | 189 | Args: | 
|  | 190 | keys_info: A dict that maps from APEX filenames to a tuple of (payload_key, | 
|  | 191 | container_key). | 
|  | 192 | key_map: A dict that overrides the keys, specified via command-line input. | 
|  | 193 |  | 
|  | 194 | Returns: | 
|  | 195 | A dict that contains the updated APEX key mapping, which should be used for | 
|  | 196 | the current signing. | 
| Tao Bao | f98fa10 | 2019-04-24 14:51:25 -0700 | [diff] [blame] | 197 |  | 
|  | 198 | Raises: | 
|  | 199 | AssertionError: On invalid container / payload key overrides. | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 200 | """ | 
|  | 201 | # Apply all the --extra_apex_payload_key options to override the payload | 
|  | 202 | # signing keys in the given keys_info. | 
|  | 203 | for apex, key in OPTIONS.extra_apex_payload_keys.items(): | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 204 | if not key: | 
|  | 205 | key = 'PRESIGNED' | 
| Tao Bao | 3422309 | 2019-07-11 11:52:52 -0700 | [diff] [blame] | 206 | if apex not in keys_info: | 
|  | 207 | logger.warning('Failed to find %s in target_files; Ignored', apex) | 
|  | 208 | continue | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 209 | keys_info[apex] = (key, keys_info[apex][1]) | 
|  | 210 |  | 
|  | 211 | # Apply the key remapping to container keys. | 
|  | 212 | for apex, (payload_key, container_key) in keys_info.items(): | 
|  | 213 | keys_info[apex] = (payload_key, key_map.get(container_key, container_key)) | 
|  | 214 |  | 
|  | 215 | # Apply all the --extra_apks options to override the container keys. | 
|  | 216 | for apex, key in OPTIONS.extra_apks.items(): | 
|  | 217 | # Skip non-APEX containers. | 
|  | 218 | if apex not in keys_info: | 
|  | 219 | continue | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 220 | if not key: | 
|  | 221 | key = 'PRESIGNED' | 
| Tao Bao | fa9de0a | 2019-03-18 10:24:17 -0700 | [diff] [blame] | 222 | keys_info[apex] = (keys_info[apex][0], key_map.get(key, key)) | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 223 |  | 
| Tao Bao | f98fa10 | 2019-04-24 14:51:25 -0700 | [diff] [blame] | 224 | # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the | 
|  | 225 | # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload | 
|  | 226 | # (overridden via commandline) indicates a config error, which should not be | 
|  | 227 | # allowed. | 
|  | 228 | for apex, (payload_key, container_key) in keys_info.items(): | 
|  | 229 | if container_key != 'PRESIGNED': | 
|  | 230 | continue | 
|  | 231 | if apex in OPTIONS.extra_apex_payload_keys: | 
|  | 232 | payload_override = OPTIONS.extra_apex_payload_keys[apex] | 
|  | 233 | assert payload_override == '', \ | 
|  | 234 | ("Invalid APEX key overrides: {} has PRESIGNED container but " | 
|  | 235 | "non-PRESIGNED payload key {}").format(apex, payload_override) | 
|  | 236 | if payload_key != 'PRESIGNED': | 
|  | 237 | print( | 
|  | 238 | "Setting {} payload as PRESIGNED due to PRESIGNED container".format( | 
|  | 239 | apex)) | 
|  | 240 | keys_info[apex] = ('PRESIGNED', 'PRESIGNED') | 
|  | 241 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 242 | return keys_info | 
|  | 243 |  | 
|  | 244 |  | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 245 | def GetApkFileInfo(filename, compressed_extension, skipped_prefixes): | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 246 | """Returns the APK info based on the given filename. | 
|  | 247 |  | 
|  | 248 | Checks if the given filename (with path) looks like an APK file, by taking the | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 249 | compressed extension into consideration. If it appears to be an APK file, | 
|  | 250 | further checks if the APK file should be skipped when signing, based on the | 
|  | 251 | given path prefixes. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 252 |  | 
|  | 253 | Args: | 
|  | 254 | filename: Path to the file. | 
|  | 255 | compressed_extension: The extension string of compressed APKs (e.g. ".gz"), | 
|  | 256 | or None if there's no compressed APKs. | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 257 | skipped_prefixes: A set/list/tuple of the path prefixes to be skipped. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 258 |  | 
|  | 259 | Returns: | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 260 | (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the | 
|  | 261 | given filename is an APK file. is_compressed indicates whether the APK file | 
|  | 262 | is compressed (only meaningful when is_apk is True). should_be_skipped | 
|  | 263 | indicates whether the filename matches any of the given prefixes to be | 
|  | 264 | skipped. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 265 |  | 
|  | 266 | Raises: | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 267 | AssertionError: On invalid compressed_extension or skipped_prefixes inputs. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 268 | """ | 
|  | 269 | assert compressed_extension is None or compressed_extension.startswith('.'), \ | 
|  | 270 | "Invalid compressed_extension arg: '{}'".format(compressed_extension) | 
|  | 271 |  | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 272 | # skipped_prefixes should be one of set/list/tuple types. Other types such as | 
|  | 273 | # str shouldn't be accepted. | 
| Tao Bao | badceb2 | 2019-03-15 09:33:43 -0700 | [diff] [blame] | 274 | assert isinstance(skipped_prefixes, (set, list, tuple)), \ | 
|  | 275 | "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes)) | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 276 |  | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 277 | compressed_apk_extension = ( | 
|  | 278 | ".apk" + compressed_extension if compressed_extension else None) | 
|  | 279 | is_apk = (filename.endswith(".apk") or | 
|  | 280 | (compressed_apk_extension and | 
|  | 281 | filename.endswith(compressed_apk_extension))) | 
|  | 282 | if not is_apk: | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 283 | return (False, False, False) | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 284 |  | 
|  | 285 | is_compressed = (compressed_apk_extension and | 
|  | 286 | filename.endswith(compressed_apk_extension)) | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 287 | should_be_skipped = filename.startswith(tuple(skipped_prefixes)) | 
|  | 288 | return (True, is_compressed, should_be_skipped) | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 289 |  | 
|  | 290 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 291 | def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys, | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 292 | compressed_extension, apex_keys): | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 293 | """Checks that all the APKs and APEXes have keys specified. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 294 |  | 
|  | 295 | Args: | 
|  | 296 | input_tf_zip: An open target_files zip file. | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 297 | known_keys: A set of APKs and APEXes that have known signing keys. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 298 | compressed_extension: The extension string of compressed APKs, such as | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 299 | '.gz', or None if there's no compressed APKs. | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 300 | apex_keys: A dict that contains the key mapping from APEX name to | 
|  | 301 | (payload_key, container_key). | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 302 |  | 
|  | 303 | Raises: | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 304 | AssertionError: On finding unknown APKs and APEXes. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 305 | """ | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 306 | unknown_files = [] | 
| Doug Zongker | eb338ef | 2009-05-20 16:50:49 -0700 | [diff] [blame] | 307 | for info in input_tf_zip.infolist(): | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 308 | # Handle APEXes first, e.g. SYSTEM/apex/com.android.tzdata.apex. | 
|  | 309 | if (info.filename.startswith('SYSTEM/apex') and | 
|  | 310 | info.filename.endswith('.apex')): | 
|  | 311 | name = os.path.basename(info.filename) | 
|  | 312 | if name not in known_keys: | 
|  | 313 | unknown_files.append(name) | 
|  | 314 | continue | 
|  | 315 |  | 
|  | 316 | # And APKs. | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 317 | (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( | 
|  | 318 | info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) | 
|  | 319 | if not is_apk or should_be_skipped: | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 320 | continue | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 321 |  | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 322 | name = os.path.basename(info.filename) | 
|  | 323 | if is_compressed: | 
|  | 324 | name = name[:-len(compressed_extension)] | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 325 | if name not in known_keys: | 
|  | 326 | unknown_files.append(name) | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 327 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 328 | assert not unknown_files, \ | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 329 | ("No key specified for:\n  {}\n" | 
|  | 330 | "Use '-e <apkname>=' to specify a key (which may be an empty string to " | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 331 | "not sign this apk).".format("\n  ".join(unknown_files))) | 
| Doug Zongker | eb338ef | 2009-05-20 16:50:49 -0700 | [diff] [blame] | 332 |  | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 333 | # For all the APEXes, double check that we won't have an APEX that has only | 
| Tao Bao | f98fa10 | 2019-04-24 14:51:25 -0700 | [diff] [blame] | 334 | # one of the payload / container keys set. Note that non-PRESIGNED container | 
|  | 335 | # with PRESIGNED payload could be allowed but currently unsupported. It would | 
|  | 336 | # require changing SignApex implementation. | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 337 | if not apex_keys: | 
|  | 338 | return | 
|  | 339 |  | 
|  | 340 | invalid_apexes = [] | 
|  | 341 | for info in input_tf_zip.infolist(): | 
|  | 342 | if (not info.filename.startswith('SYSTEM/apex') or | 
|  | 343 | not info.filename.endswith('.apex')): | 
|  | 344 | continue | 
|  | 345 |  | 
|  | 346 | name = os.path.basename(info.filename) | 
|  | 347 | (payload_key, container_key) = apex_keys[name] | 
|  | 348 | if ((payload_key in common.SPECIAL_CERT_STRINGS and | 
|  | 349 | container_key not in common.SPECIAL_CERT_STRINGS) or | 
|  | 350 | (payload_key not in common.SPECIAL_CERT_STRINGS and | 
|  | 351 | container_key in common.SPECIAL_CERT_STRINGS)): | 
|  | 352 | invalid_apexes.append( | 
|  | 353 | "{}: payload_key {}, container_key {}".format( | 
|  | 354 | name, payload_key, container_key)) | 
|  | 355 |  | 
|  | 356 | assert not invalid_apexes, \ | 
|  | 357 | "Invalid APEX keys specified:\n  {}\n".format( | 
|  | 358 | "\n  ".join(invalid_apexes)) | 
|  | 359 |  | 
| Doug Zongker | eb338ef | 2009-05-20 16:50:49 -0700 | [diff] [blame] | 360 |  | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 361 | def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map, | 
|  | 362 | is_compressed): | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 363 | unsigned = tempfile.NamedTemporaryFile() | 
|  | 364 | unsigned.write(data) | 
|  | 365 | unsigned.flush() | 
|  | 366 |  | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 367 | if is_compressed: | 
|  | 368 | uncompressed = tempfile.NamedTemporaryFile() | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 369 | with gzip.open(unsigned.name, "rb") as in_file, \ | 
|  | 370 | open(uncompressed.name, "wb") as out_file: | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 371 | shutil.copyfileobj(in_file, out_file) | 
|  | 372 |  | 
|  | 373 | # Finally, close the "unsigned" file (which is gzip compressed), and then | 
|  | 374 | # replace it with the uncompressed version. | 
|  | 375 | # | 
|  | 376 | # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use, | 
|  | 377 | # we could just gzip / gunzip in-memory buffers instead. | 
|  | 378 | unsigned.close() | 
|  | 379 | unsigned = uncompressed | 
|  | 380 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 381 | signed = tempfile.NamedTemporaryFile() | 
|  | 382 |  | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 383 | # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's | 
|  | 384 | # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK | 
|  | 385 | # didn't change, we don't want its signature to change due to the switch | 
|  | 386 | # from SHA-1 to SHA-256. | 
|  | 387 | # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion | 
|  | 388 | # is 18 or higher. For pre-N builds we disable this mechanism by pretending | 
|  | 389 | # that the APK's minSdkVersion is 1. | 
|  | 390 | # For N+ builds, we let APK signer rely on the APK's minSdkVersion to | 
|  | 391 | # determine whether to use SHA-256. | 
|  | 392 | min_api_level = None | 
|  | 393 | if platform_api_level > 23: | 
|  | 394 | # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's | 
|  | 395 | # minSdkVersion attribute | 
|  | 396 | min_api_level = None | 
|  | 397 | else: | 
|  | 398 | # Force APK signer to use SHA-1 | 
|  | 399 | min_api_level = 1 | 
|  | 400 |  | 
|  | 401 | common.SignFile(unsigned.name, signed.name, keyname, pw, | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 402 | min_api_level=min_api_level, | 
|  | 403 | codename_to_api_level_map=codename_to_api_level_map) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 404 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 405 | data = None | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 406 | if is_compressed: | 
|  | 407 | # Recompress the file after it has been signed. | 
|  | 408 | compressed = tempfile.NamedTemporaryFile() | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 409 | with open(signed.name, "rb") as in_file, \ | 
|  | 410 | gzip.open(compressed.name, "wb") as out_file: | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 411 | shutil.copyfileobj(in_file, out_file) | 
|  | 412 |  | 
|  | 413 | data = compressed.read() | 
|  | 414 | compressed.close() | 
|  | 415 | else: | 
|  | 416 | data = signed.read() | 
|  | 417 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 418 | unsigned.close() | 
|  | 419 | signed.close() | 
|  | 420 |  | 
|  | 421 | return data | 
|  | 422 |  | 
|  | 423 |  | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 424 | def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 425 | apk_keys, apex_keys, key_passwords, | 
|  | 426 | platform_api_level, codename_to_api_level_map, | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 427 | compressed_extension): | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 428 | # maxsize measures the maximum filename length, including the ones to be | 
|  | 429 | # skipped. | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 430 | maxsize = max( | 
|  | 431 | [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist() | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 432 | if GetApkFileInfo(i.filename, compressed_extension, [])[0]]) | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 433 | system_root_image = misc_info.get("system_root_image") == "true" | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 434 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 435 | for info in input_tf_zip.infolist(): | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 436 | filename = info.filename | 
|  | 437 | if filename.startswith("IMAGES/"): | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 438 | continue | 
| Doug Zongker | 3c84f56 | 2014-07-31 11:06:30 -0700 | [diff] [blame] | 439 |  | 
| Tao Bao | 0480850 | 2019-07-25 23:11:41 -0700 | [diff] [blame] | 440 | # Skip OTA-specific images (e.g. split super images), which will be | 
|  | 441 | # re-generated during signing. | 
| Tao Bao | 33bf268 | 2019-01-11 12:37:35 -0800 | [diff] [blame] | 442 | if filename.startswith("OTA/") and filename.endswith(".img"): | 
|  | 443 | continue | 
|  | 444 |  | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 445 | data = input_tf_zip.read(filename) | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 446 | out_info = copy.copy(info) | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 447 | (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo( | 
|  | 448 | filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix) | 
|  | 449 |  | 
|  | 450 | if is_apk and should_be_skipped: | 
|  | 451 | # Copy skipped APKs verbatim. | 
|  | 452 | print( | 
|  | 453 | "NOT signing: %s\n" | 
|  | 454 | "        (skipped due to matching prefix)" % (filename,)) | 
|  | 455 | common.ZipWriteStr(output_tf_zip, out_info, data) | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 456 |  | 
| Tao Bao | f2cffbd | 2015-07-22 12:33:18 -0700 | [diff] [blame] | 457 | # Sign APKs. | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 458 | elif is_apk: | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 459 | name = os.path.basename(filename) | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 460 | if is_compressed: | 
|  | 461 | name = name[:-len(compressed_extension)] | 
|  | 462 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 463 | key = apk_keys[name] | 
| Doug Zongker | f6a53aa | 2009-12-15 15:06:55 -0800 | [diff] [blame] | 464 | if key not in common.SPECIAL_CERT_STRINGS: | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 465 | print("    signing: %-*s (%s)" % (maxsize, name, key)) | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 466 | signed_data = SignApk(data, key, key_passwords[key], platform_api_level, | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 467 | codename_to_api_level_map, is_compressed) | 
| Tao Bao | 2ed665a | 2015-04-01 11:21:55 -0700 | [diff] [blame] | 468 | common.ZipWriteStr(output_tf_zip, out_info, signed_data) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 469 | else: | 
|  | 470 | # an APK we're not supposed to sign. | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 471 | print( | 
|  | 472 | "NOT signing: %s\n" | 
|  | 473 | "        (skipped due to special cert string)" % (name,)) | 
| Tao Bao | 2ed665a | 2015-04-01 11:21:55 -0700 | [diff] [blame] | 474 | common.ZipWriteStr(output_tf_zip, out_info, data) | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 475 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 476 | # Sign bundled APEX files. | 
|  | 477 | elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"): | 
|  | 478 | name = os.path.basename(filename) | 
|  | 479 | payload_key, container_key = apex_keys[name] | 
|  | 480 |  | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 481 | # We've asserted not having a case with only one of them PRESIGNED. | 
|  | 482 | if (payload_key not in common.SPECIAL_CERT_STRINGS and | 
|  | 483 | container_key not in common.SPECIAL_CERT_STRINGS): | 
|  | 484 | print("    signing: %-*s container (%s)" % ( | 
|  | 485 | maxsize, name, container_key)) | 
|  | 486 | print("           : %-*s payload   (%s)" % ( | 
|  | 487 | maxsize, name, payload_key)) | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 488 |  | 
| Tao Bao | e7354ba | 2019-05-09 16:54:15 -0700 | [diff] [blame] | 489 | signed_apex = apex_utils.SignApex( | 
| Tao Bao | 1ac886e | 2019-06-26 11:58:22 -0700 | [diff] [blame] | 490 | misc_info['avb_avbtool'], | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 491 | data, | 
|  | 492 | payload_key, | 
|  | 493 | container_key, | 
|  | 494 | key_passwords[container_key], | 
|  | 495 | codename_to_api_level_map, | 
| Tao Bao | 448004a | 2019-09-19 07:55:02 -0700 | [diff] [blame] | 496 | no_hashtree=True, | 
|  | 497 | signing_args=OPTIONS.avb_extra_args.get('apex')) | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 498 | common.ZipWrite(output_tf_zip, signed_apex, filename) | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 499 |  | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 500 | else: | 
|  | 501 | print( | 
|  | 502 | "NOT signing: %s\n" | 
|  | 503 | "        (skipped due to special cert string)" % (name,)) | 
|  | 504 | common.ZipWriteStr(output_tf_zip, out_info, data) | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 505 |  | 
|  | 506 | # AVB public keys for the installed APEXes, which will be updated later. | 
|  | 507 | elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex' and | 
|  | 508 | filename != 'SYSTEM/etc/security/apex/'): | 
|  | 509 | continue | 
|  | 510 |  | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 511 | # System properties. | 
| Tao Bao | 338c1b7 | 2019-06-21 09:38:24 -0700 | [diff] [blame] | 512 | elif filename in ( | 
|  | 513 | "SYSTEM/build.prop", | 
|  | 514 |  | 
|  | 515 | "VENDOR/build.prop", | 
|  | 516 | "SYSTEM/vendor/build.prop", | 
|  | 517 |  | 
|  | 518 | "ODM/etc/build.prop", | 
|  | 519 | "VENDOR/odm/etc/build.prop", | 
|  | 520 |  | 
|  | 521 | "PRODUCT/build.prop", | 
|  | 522 | "SYSTEM/product/build.prop", | 
|  | 523 |  | 
| Justin Yun | 6151e3f | 2019-06-25 15:58:13 +0900 | [diff] [blame] | 524 | "SYSTEM_EXT/build.prop", | 
|  | 525 | "SYSTEM/system_ext/build.prop", | 
| Tao Bao | 338c1b7 | 2019-06-21 09:38:24 -0700 | [diff] [blame] | 526 |  | 
|  | 527 | "SYSTEM/etc/prop.default", | 
|  | 528 | "BOOT/RAMDISK/prop.default", | 
|  | 529 | "RECOVERY/RAMDISK/prop.default", | 
|  | 530 |  | 
|  | 531 | # ROOT/default.prop is a legacy path, but may still exist for upgrading | 
|  | 532 | # devices that don't support `property_overrides_split_enabled`. | 
|  | 533 | "ROOT/default.prop", | 
|  | 534 |  | 
|  | 535 | # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist | 
|  | 536 | # as a symlink in the current code. So it's a no-op here. Keeping the | 
|  | 537 | # path here for clarity. | 
|  | 538 | "RECOVERY/RAMDISK/default.prop"): | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 539 | print("Rewriting %s:" % (filename,)) | 
| Hung-ying Tyan | 7eb6a92 | 2017-05-01 21:56:26 +0800 | [diff] [blame] | 540 | if stat.S_ISLNK(info.external_attr >> 16): | 
|  | 541 | new_data = data | 
|  | 542 | else: | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 543 | new_data = RewriteProps(data.decode()) | 
| Tao Bao | 2ed665a | 2015-04-01 11:21:55 -0700 | [diff] [blame] | 544 | common.ZipWriteStr(output_tf_zip, out_info, new_data) | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 545 |  | 
| Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 546 | # Replace the certs in *mac_permissions.xml (there could be multiple, such | 
|  | 547 | # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml). | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 548 | elif filename.endswith("mac_permissions.xml"): | 
|  | 549 | print("Rewriting %s with new keys." % (filename,)) | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 550 | new_data = ReplaceCerts(data.decode()) | 
| Tao Bao | 2ed665a | 2015-04-01 11:21:55 -0700 | [diff] [blame] | 551 | common.ZipWriteStr(output_tf_zip, out_info, new_data) | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 552 |  | 
| Tianjie Xu | 616fbeb | 2017-05-23 14:51:02 -0700 | [diff] [blame] | 553 | # Ask add_img_to_target_files to rebuild the recovery patch if needed. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 554 | elif filename in ("SYSTEM/recovery-from-boot.p", | 
|  | 555 | "SYSTEM/etc/recovery.img", | 
|  | 556 | "SYSTEM/bin/install-recovery.sh"): | 
| Tianjie Xu | 616fbeb | 2017-05-23 14:51:02 -0700 | [diff] [blame] | 557 | OPTIONS.rebuild_recovery = True | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 558 |  | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 559 | # Don't copy OTA certs if we're replacing them. | 
| Tianjie Xu | 2df23d7 | 2019-10-15 18:06:25 -0700 | [diff] [blame] | 560 | # Replacement of update-payload-key.pub.pem was removed in b/116660991. | 
| Tao Bao | 696bb33 | 2018-08-17 16:27:01 -0700 | [diff] [blame] | 561 | elif ( | 
|  | 562 | OPTIONS.replace_ota_keys and | 
|  | 563 | filename in ( | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 564 | "BOOT/RAMDISK/system/etc/security/otacerts.zip", | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 565 | "RECOVERY/RAMDISK/system/etc/security/otacerts.zip", | 
| Tianjie Xu | 2df23d7 | 2019-10-15 18:06:25 -0700 | [diff] [blame] | 566 | "SYSTEM/etc/security/otacerts.zip")): | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 567 | pass | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 568 |  | 
| Tao Bao | 46a5999 | 2017-06-05 11:55:16 -0700 | [diff] [blame] | 569 | # Skip META/misc_info.txt since we will write back the new values later. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 570 | elif filename == "META/misc_info.txt": | 
| Geremy Condra | f19b365 | 2014-07-29 17:54:54 -0700 | [diff] [blame] | 571 | pass | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 572 |  | 
|  | 573 | # Skip verity public key if we will replace it. | 
| Michael Runge | 947894f | 2014-10-14 20:58:38 -0700 | [diff] [blame] | 574 | elif (OPTIONS.replace_verity_public_key and | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 575 | filename in ("BOOT/RAMDISK/verity_key", | 
|  | 576 | "ROOT/verity_key")): | 
| Geremy Condra | f19b365 | 2014-07-29 17:54:54 -0700 | [diff] [blame] | 577 | pass | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 578 |  | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 579 | # Skip verity keyid (for system_root_image use) if we will replace it. | 
| Tao Bao | 11f955c | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 580 | elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline": | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 581 | pass | 
|  | 582 |  | 
| Tianjie Xu | 4f09900 | 2016-08-11 18:04:27 -0700 | [diff] [blame] | 583 | # Skip the care_map as we will regenerate the system/vendor images. | 
| Tianjie Xu | 4c05f4a | 2018-09-14 16:24:41 -0700 | [diff] [blame] | 584 | elif filename == "META/care_map.pb" or filename == "META/care_map.txt": | 
| Tianjie Xu | 4f09900 | 2016-08-11 18:04:27 -0700 | [diff] [blame] | 585 | pass | 
|  | 586 |  | 
| Bowgo Tsai | e4544b1 | 2019-02-27 10:15:51 +0800 | [diff] [blame] | 587 | # Updates system_other.avbpubkey in /product/etc/. | 
|  | 588 | elif filename in ( | 
|  | 589 | "PRODUCT/etc/security/avb/system_other.avbpubkey", | 
|  | 590 | "SYSTEM/product/etc/security/avb/system_other.avbpubkey"): | 
|  | 591 | # Only update system_other's public key, if the corresponding signing | 
|  | 592 | # key is specified via --avb_system_other_key. | 
|  | 593 | signing_key = OPTIONS.avb_keys.get("system_other") | 
|  | 594 | if signing_key: | 
| Tao Bao | 1ac886e | 2019-06-26 11:58:22 -0700 | [diff] [blame] | 595 | public_key = common.ExtractAvbPublicKey( | 
|  | 596 | misc_info['avb_avbtool'], signing_key) | 
| Bowgo Tsai | e4544b1 | 2019-02-27 10:15:51 +0800 | [diff] [blame] | 597 | print("    Rewriting AVB public key of system_other in /product") | 
|  | 598 | common.ZipWrite(output_tf_zip, public_key, filename) | 
|  | 599 |  | 
| Bowgo Tsai | 78369eb | 2019-04-23 12:28:44 +0800 | [diff] [blame] | 600 | # Should NOT sign boot-debug.img. | 
|  | 601 | elif filename in ( | 
|  | 602 | "BOOT/RAMDISK/force_debuggable", | 
|  | 603 | "RECOVERY/RAMDISK/force_debuggable" | 
|  | 604 | "RECOVERY/RAMDISK/first_stage_ramdisk/force_debuggable"): | 
|  | 605 | raise common.ExternalError("debuggable boot.img cannot be signed") | 
|  | 606 |  | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 607 | # A non-APK file; copy it verbatim. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 608 | else: | 
| Tao Bao | 2ed665a | 2015-04-01 11:21:55 -0700 | [diff] [blame] | 609 | common.ZipWriteStr(output_tf_zip, out_info, data) | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 610 |  | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 611 | if OPTIONS.replace_ota_keys: | 
| Tianjie Xu | 616fbeb | 2017-05-23 14:51:02 -0700 | [diff] [blame] | 612 | ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info) | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 613 |  | 
| Tao Bao | 46a5999 | 2017-06-05 11:55:16 -0700 | [diff] [blame] | 614 | # Replace the keyid string in misc_info dict. | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 615 | if OPTIONS.replace_verity_private_key: | 
| Tao Bao | 46a5999 | 2017-06-05 11:55:16 -0700 | [diff] [blame] | 616 | ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1]) | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 617 |  | 
|  | 618 | if OPTIONS.replace_verity_public_key: | 
| Tao Bao | c998193 | 2019-09-16 12:10:43 -0700 | [diff] [blame] | 619 | # Replace the one in root dir in system.img. | 
| Tianjie Xu | 616fbeb | 2017-05-23 14:51:02 -0700 | [diff] [blame] | 620 | ReplaceVerityPublicKey( | 
| Tao Bao | c998193 | 2019-09-16 12:10:43 -0700 | [diff] [blame] | 621 | output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1]) | 
|  | 622 |  | 
|  | 623 | if not system_root_image: | 
|  | 624 | # Additionally replace the copy in ramdisk if not using system-as-root. | 
|  | 625 | ReplaceVerityPublicKey( | 
|  | 626 | output_tf_zip, | 
|  | 627 | 'BOOT/RAMDISK/verity_key', | 
|  | 628 | OPTIONS.replace_verity_public_key[1]) | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 629 |  | 
|  | 630 | # Replace the keyid string in BOOT/cmdline. | 
|  | 631 | if OPTIONS.replace_verity_keyid: | 
| Tianjie Xu | 616fbeb | 2017-05-23 14:51:02 -0700 | [diff] [blame] | 632 | ReplaceVerityKeyId(input_tf_zip, output_tf_zip, | 
|  | 633 | OPTIONS.replace_verity_keyid[1]) | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 634 |  | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 635 | # Replace the AVB signing keys, if any. | 
|  | 636 | ReplaceAvbSigningKeys(misc_info) | 
|  | 637 |  | 
| Tao Bao | 19b02fe | 2019-10-09 00:04:28 -0700 | [diff] [blame] | 638 | # Rewrite the props in AVB signing args. | 
|  | 639 | if misc_info.get('avb_enable') == 'true': | 
|  | 640 | RewriteAvbProps(misc_info) | 
|  | 641 |  | 
| Tao Bao | 46a5999 | 2017-06-05 11:55:16 -0700 | [diff] [blame] | 642 | # Write back misc_info with the latest values. | 
|  | 643 | ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info) | 
|  | 644 |  | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 645 |  | 
| Robert Craig | 817c574 | 2013-04-19 10:59:22 -0400 | [diff] [blame] | 646 | def ReplaceCerts(data): | 
| Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 647 | """Replaces all the occurences of X.509 certs with the new ones. | 
| Robert Craig | 817c574 | 2013-04-19 10:59:22 -0400 | [diff] [blame] | 648 |  | 
| Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 649 | The mapping info is read from OPTIONS.key_map. Non-existent certificate will | 
|  | 650 | be skipped. After the replacement, it additionally checks for duplicate | 
|  | 651 | entries, which would otherwise fail the policy loading code in | 
|  | 652 | frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java. | 
|  | 653 |  | 
|  | 654 | Args: | 
|  | 655 | data: Input string that contains a set of X.509 certs. | 
|  | 656 |  | 
|  | 657 | Returns: | 
|  | 658 | A string after the replacement. | 
|  | 659 |  | 
|  | 660 | Raises: | 
|  | 661 | AssertionError: On finding duplicate entries. | 
|  | 662 | """ | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 663 | for old, new in OPTIONS.key_map.items(): | 
| Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 664 | if OPTIONS.verbose: | 
|  | 665 | print("    Replacing %s.x509.pem with %s.x509.pem" % (old, new)) | 
|  | 666 |  | 
|  | 667 | try: | 
|  | 668 | with open(old + ".x509.pem") as old_fp: | 
|  | 669 | old_cert16 = base64.b16encode( | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 670 | common.ParseCertificate(old_fp.read())).decode().lower() | 
| Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 671 | with open(new + ".x509.pem") as new_fp: | 
|  | 672 | new_cert16 = base64.b16encode( | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 673 | common.ParseCertificate(new_fp.read())).decode().lower() | 
| Tao Bao | 6647263 | 2017-12-04 17:16:36 -0800 | [diff] [blame] | 674 | except IOError as e: | 
|  | 675 | if OPTIONS.verbose or e.errno != errno.ENOENT: | 
|  | 676 | print("    Error accessing %s: %s.\nSkip replacing %s.x509.pem with " | 
|  | 677 | "%s.x509.pem." % (e.filename, e.strerror, old, new)) | 
|  | 678 | continue | 
|  | 679 |  | 
|  | 680 | # Only match entire certs. | 
|  | 681 | pattern = "\\b" + old_cert16 + "\\b" | 
|  | 682 | (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE) | 
|  | 683 |  | 
|  | 684 | if OPTIONS.verbose: | 
|  | 685 | print("    Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % ( | 
|  | 686 | num, old, new)) | 
|  | 687 |  | 
|  | 688 | # Verify that there're no duplicate entries after the replacement. Note that | 
|  | 689 | # it's only checking entries with global seinfo at the moment (i.e. ignoring | 
|  | 690 | # the ones with inner packages). (Bug: 69479366) | 
|  | 691 | root = ElementTree.fromstring(data) | 
|  | 692 | signatures = [signer.attrib['signature'] for signer in root.findall('signer')] | 
|  | 693 | assert len(signatures) == len(set(signatures)), \ | 
|  | 694 | "Found duplicate entries after cert replacement: {}".format(data) | 
| Robert Craig | 817c574 | 2013-04-19 10:59:22 -0400 | [diff] [blame] | 695 |  | 
|  | 696 | return data | 
|  | 697 |  | 
|  | 698 |  | 
| Doug Zongker | c09abc8 | 2010-01-11 13:09:15 -0800 | [diff] [blame] | 699 | def EditTags(tags): | 
| Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 700 | """Applies the edits to the tag string as specified in OPTIONS.tag_changes. | 
|  | 701 |  | 
|  | 702 | Args: | 
|  | 703 | tags: The input string that contains comma-separated tags. | 
|  | 704 |  | 
|  | 705 | Returns: | 
|  | 706 | The updated tags (comma-separated and sorted). | 
|  | 707 | """ | 
| Doug Zongker | c09abc8 | 2010-01-11 13:09:15 -0800 | [diff] [blame] | 708 | tags = set(tags.split(",")) | 
|  | 709 | for ch in OPTIONS.tag_changes: | 
|  | 710 | if ch[0] == "-": | 
|  | 711 | tags.discard(ch[1:]) | 
|  | 712 | elif ch[0] == "+": | 
|  | 713 | tags.add(ch[1:]) | 
|  | 714 | return ",".join(sorted(tags)) | 
|  | 715 |  | 
|  | 716 |  | 
| Tao Bao | a7054ee | 2017-12-08 14:42:16 -0800 | [diff] [blame] | 717 | def RewriteProps(data): | 
|  | 718 | """Rewrites the system properties in the given string. | 
|  | 719 |  | 
|  | 720 | Each property is expected in 'key=value' format. The properties that contain | 
|  | 721 | build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling | 
|  | 722 | EditTags(). | 
|  | 723 |  | 
|  | 724 | Args: | 
|  | 725 | data: Input string, separated by newlines. | 
|  | 726 |  | 
|  | 727 | Returns: | 
|  | 728 | The string with modified properties. | 
|  | 729 | """ | 
| Doug Zongker | 17aa944 | 2009-04-17 10:15:58 -0700 | [diff] [blame] | 730 | output = [] | 
|  | 731 | for line in data.split("\n"): | 
|  | 732 | line = line.strip() | 
|  | 733 | original_line = line | 
| Michael Runge | dc2661a | 2014-06-03 14:43:11 -0700 | [diff] [blame] | 734 | if line and line[0] != '#' and "=" in line: | 
| Doug Zongker | 17aa944 | 2009-04-17 10:15:58 -0700 | [diff] [blame] | 735 | key, value = line.split("=", 1) | 
| Magnus Strandh | 234f4b4 | 2019-05-01 23:09:30 +0200 | [diff] [blame] | 736 | if (key.startswith("ro.") and | 
|  | 737 | key.endswith((".build.fingerprint", ".build.thumbprint"))): | 
| Doug Zongker | c09abc8 | 2010-01-11 13:09:15 -0800 | [diff] [blame] | 738 | pieces = value.split("/") | 
|  | 739 | pieces[-1] = EditTags(pieces[-1]) | 
|  | 740 | value = "/".join(pieces) | 
| Tao Bao | cb7ff77 | 2015-09-11 15:27:56 -0700 | [diff] [blame] | 741 | elif key == "ro.bootimage.build.fingerprint": | 
|  | 742 | pieces = value.split("/") | 
|  | 743 | pieces[-1] = EditTags(pieces[-1]) | 
|  | 744 | value = "/".join(pieces) | 
| Doug Zongker | 17aa944 | 2009-04-17 10:15:58 -0700 | [diff] [blame] | 745 | elif key == "ro.build.description": | 
| Doug Zongker | c09abc8 | 2010-01-11 13:09:15 -0800 | [diff] [blame] | 746 | pieces = value.split(" ") | 
| Doug Zongker | 17aa944 | 2009-04-17 10:15:58 -0700 | [diff] [blame] | 747 | assert len(pieces) == 5 | 
| Doug Zongker | c09abc8 | 2010-01-11 13:09:15 -0800 | [diff] [blame] | 748 | pieces[-1] = EditTags(pieces[-1]) | 
|  | 749 | value = " ".join(pieces) | 
| Magnus Strandh | 234f4b4 | 2019-05-01 23:09:30 +0200 | [diff] [blame] | 750 | elif key.startswith("ro.") and key.endswith(".build.tags"): | 
| Doug Zongker | c09abc8 | 2010-01-11 13:09:15 -0800 | [diff] [blame] | 751 | value = EditTags(value) | 
| Doug Zongker | a8608a7 | 2013-07-23 11:51:04 -0700 | [diff] [blame] | 752 | elif key == "ro.build.display.id": | 
|  | 753 | # change, eg, "JWR66N dev-keys" to "JWR66N" | 
|  | 754 | value = value.split() | 
| Michael Runge | dc2661a | 2014-06-03 14:43:11 -0700 | [diff] [blame] | 755 | if len(value) > 1 and value[-1].endswith("-keys"): | 
| Andrew Boie | 73d5abb | 2013-12-11 12:42:03 -0800 | [diff] [blame] | 756 | value.pop() | 
|  | 757 | value = " ".join(value) | 
| Doug Zongker | c09abc8 | 2010-01-11 13:09:15 -0800 | [diff] [blame] | 758 | line = key + "=" + value | 
| Doug Zongker | 17aa944 | 2009-04-17 10:15:58 -0700 | [diff] [blame] | 759 | if line != original_line: | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 760 | print("  replace: ", original_line) | 
|  | 761 | print("     with: ", line) | 
| Doug Zongker | 17aa944 | 2009-04-17 10:15:58 -0700 | [diff] [blame] | 762 | output.append(line) | 
|  | 763 | return "\n".join(output) + "\n" | 
|  | 764 |  | 
|  | 765 |  | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 766 | def WriteOtacerts(output_zip, filename, keys): | 
|  | 767 | """Constructs a zipfile from given keys; and writes it to output_zip. | 
|  | 768 |  | 
|  | 769 | Args: | 
|  | 770 | output_zip: The output target_files zip. | 
|  | 771 | filename: The archive name in the output zip. | 
|  | 772 | keys: A list of public keys to use during OTA package verification. | 
|  | 773 | """ | 
| Tao Bao | bb73388 | 2019-07-24 23:31:19 -0700 | [diff] [blame] | 774 | temp_file = io.BytesIO() | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 775 | certs_zip = zipfile.ZipFile(temp_file, "w") | 
|  | 776 | for k in keys: | 
|  | 777 | common.ZipWrite(certs_zip, k) | 
|  | 778 | common.ZipClose(certs_zip) | 
|  | 779 | common.ZipWriteStr(output_zip, filename, temp_file.getvalue()) | 
|  | 780 |  | 
|  | 781 |  | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 782 | def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 783 | try: | 
|  | 784 | keylist = input_tf_zip.read("META/otakeys.txt").split() | 
|  | 785 | except KeyError: | 
| T.R. Fullhart | a28acc6 | 2013-03-18 10:31:26 -0700 | [diff] [blame] | 786 | raise common.ExternalError("can't read META/otakeys.txt from input") | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 787 |  | 
| Tao Bao | f718f90 | 2017-11-09 10:10:10 -0800 | [diff] [blame] | 788 | extra_recovery_keys = misc_info.get("extra_recovery_keys") | 
| Doug Zongker | e121d6a | 2011-02-01 14:13:52 -0800 | [diff] [blame] | 789 | if extra_recovery_keys: | 
|  | 790 | extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" | 
|  | 791 | for k in extra_recovery_keys.split()] | 
|  | 792 | if extra_recovery_keys: | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 793 | print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys)) | 
| Doug Zongker | e121d6a | 2011-02-01 14:13:52 -0800 | [diff] [blame] | 794 | else: | 
|  | 795 | extra_recovery_keys = [] | 
|  | 796 |  | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 797 | mapped_keys = [] | 
|  | 798 | for k in keylist: | 
|  | 799 | m = re.match(r"^(.*)\.x509\.pem$", k) | 
|  | 800 | if not m: | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 801 | raise common.ExternalError( | 
|  | 802 | "can't parse \"%s\" from META/otakeys.txt" % (k,)) | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 803 | k = m.group(1) | 
|  | 804 | mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") | 
|  | 805 |  | 
| Doug Zongker | e05628c | 2009-08-20 17:38:42 -0700 | [diff] [blame] | 806 | if mapped_keys: | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 807 | print("using:\n   ", "\n   ".join(mapped_keys)) | 
|  | 808 | print("for OTA package verification") | 
| Doug Zongker | e05628c | 2009-08-20 17:38:42 -0700 | [diff] [blame] | 809 | else: | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 810 | devkey = misc_info.get("default_system_dev_certificate", | 
| Dan Willemsen | 0ab1be6 | 2019-04-09 21:35:37 -0700 | [diff] [blame] | 811 | "build/make/target/product/security/testkey") | 
| Tao Bao | f718f90 | 2017-11-09 10:10:10 -0800 | [diff] [blame] | 812 | mapped_devkey = OPTIONS.key_map.get(devkey, devkey) | 
|  | 813 | if mapped_devkey != devkey: | 
|  | 814 | misc_info["default_system_dev_certificate"] = mapped_devkey | 
|  | 815 | mapped_keys.append(mapped_devkey + ".x509.pem") | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 816 | print("META/otakeys.txt has no keys; using %s for OTA package" | 
|  | 817 | " verification." % (mapped_keys[0],)) | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 818 |  | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 819 | # recovery now uses the same x509.pem version of the keys. | 
| Doug Zongker | e121d6a | 2011-02-01 14:13:52 -0800 | [diff] [blame] | 820 | # extra_recovery_keys are used only in recovery. | 
| Tom Cherry | 2929cad | 2018-09-20 11:04:37 -0700 | [diff] [blame] | 821 | if misc_info.get("recovery_as_boot") == "true": | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 822 | recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip" | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 823 | else: | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 824 | recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip" | 
|  | 825 |  | 
|  | 826 | WriteOtacerts(output_tf_zip, recovery_keys_location, | 
|  | 827 | mapped_keys + extra_recovery_keys) | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 828 |  | 
|  | 829 | # SystemUpdateActivity uses the x509.pem version of the keys, but | 
|  | 830 | # put into a zipfile system/etc/security/otacerts.zip. | 
| Doug Zongker | e121d6a | 2011-02-01 14:13:52 -0800 | [diff] [blame] | 831 | # We DO NOT include the extra_recovery_keys (if any) here. | 
| Tianjie Xu | ffbe6b9 | 2018-10-19 14:34:15 -0700 | [diff] [blame] | 832 | WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 833 |  | 
| Tao Bao | a80ed22 | 2016-06-16 14:41:24 -0700 | [diff] [blame] | 834 |  | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 835 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 836 | def ReplaceVerityPublicKey(output_zip, filename, key_path): | 
|  | 837 | """Replaces the verity public key at the given path in the given zip. | 
|  | 838 |  | 
|  | 839 | Args: | 
|  | 840 | output_zip: The output target_files zip. | 
|  | 841 | filename: The archive name in the output zip. | 
|  | 842 | key_path: The path to the public key. | 
|  | 843 | """ | 
|  | 844 | print("Replacing verity public key with %s" % (key_path,)) | 
|  | 845 | common.ZipWrite(output_zip, key_path, arcname=filename) | 
| Geremy Condra | f19b365 | 2014-07-29 17:54:54 -0700 | [diff] [blame] | 846 |  | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 847 |  | 
| Tao Bao | 46a5999 | 2017-06-05 11:55:16 -0700 | [diff] [blame] | 848 | def ReplaceVerityPrivateKey(misc_info, key_path): | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 849 | """Replaces the verity private key in misc_info dict. | 
|  | 850 |  | 
|  | 851 | Args: | 
|  | 852 | misc_info: The info dict. | 
|  | 853 | key_path: The path to the private key in PKCS#8 format. | 
|  | 854 | """ | 
|  | 855 | print("Replacing verity private key with %s" % (key_path,)) | 
| Andrew Boie | d083f0b | 2014-09-15 16:01:07 -0700 | [diff] [blame] | 856 | misc_info["verity_key"] = key_path | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 857 |  | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 858 |  | 
| Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 859 | def ReplaceVerityKeyId(input_zip, output_zip, key_path): | 
|  | 860 | """Replaces the veritykeyid parameter in BOOT/cmdline. | 
|  | 861 |  | 
|  | 862 | Args: | 
|  | 863 | input_zip: The input target_files zip, which should be already open. | 
|  | 864 | output_zip: The output target_files zip, which should be already open and | 
|  | 865 | writable. | 
|  | 866 | key_path: The path to the PEM encoded X.509 certificate. | 
|  | 867 | """ | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 868 | in_cmdline = input_zip.read("BOOT/cmdline").decode() | 
| Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 869 | # Copy in_cmdline to output_zip if veritykeyid is not present. | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 870 | if "veritykeyid" not in in_cmdline: | 
| Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 871 | common.ZipWriteStr(output_zip, "BOOT/cmdline", in_cmdline) | 
|  | 872 | return | 
|  | 873 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 874 | out_buffer = [] | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 875 | for param in in_cmdline.split(): | 
| Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 876 | if "veritykeyid" not in param: | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 877 | out_buffer.append(param) | 
| Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 878 | continue | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 879 |  | 
| Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 880 | # Extract keyid using openssl command. | 
|  | 881 | p = common.Run(["openssl", "x509", "-in", key_path, "-text"], | 
| Tao Bao | de1d479 | 2018-02-20 10:05:46 -0800 | [diff] [blame] | 882 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 
| Tao Bao | e838d14 | 2017-12-23 23:44:48 -0800 | [diff] [blame] | 883 | keyid, stderr = p.communicate() | 
|  | 884 | assert p.returncode == 0, "Failed to dump certificate: {}".format(stderr) | 
|  | 885 | keyid = re.search( | 
|  | 886 | r'keyid:([0-9a-fA-F:]*)', keyid).group(1).replace(':', '').lower() | 
|  | 887 | print("Replacing verity keyid with {}".format(keyid)) | 
|  | 888 | out_buffer.append("veritykeyid=id:%s" % (keyid,)) | 
|  | 889 |  | 
|  | 890 | out_cmdline = ' '.join(out_buffer).strip() + '\n' | 
|  | 891 | common.ZipWriteStr(output_zip, "BOOT/cmdline", out_cmdline) | 
| Tao Bao | 46a5999 | 2017-06-05 11:55:16 -0700 | [diff] [blame] | 892 |  | 
|  | 893 |  | 
|  | 894 | def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info): | 
|  | 895 | """Replaces META/misc_info.txt. | 
|  | 896 |  | 
|  | 897 | Only writes back the ones in the original META/misc_info.txt. Because the | 
|  | 898 | current in-memory dict contains additional items computed at runtime. | 
|  | 899 | """ | 
|  | 900 | misc_info_old = common.LoadDictionaryFromLines( | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 901 | input_zip.read('META/misc_info.txt').decode().split('\n')) | 
| Tao Bao | 46a5999 | 2017-06-05 11:55:16 -0700 | [diff] [blame] | 902 | items = [] | 
|  | 903 | for key in sorted(misc_info): | 
|  | 904 | if key in misc_info_old: | 
|  | 905 | items.append('%s=%s' % (key, misc_info[key])) | 
|  | 906 | common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items)) | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 907 |  | 
| Tao Bao | 8adcfd1 | 2016-06-17 17:01:22 -0700 | [diff] [blame] | 908 |  | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 909 | def ReplaceAvbSigningKeys(misc_info): | 
|  | 910 | """Replaces the AVB signing keys.""" | 
|  | 911 |  | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 912 | def ReplaceAvbPartitionSigningKey(partition): | 
|  | 913 | key = OPTIONS.avb_keys.get(partition) | 
|  | 914 | if not key: | 
|  | 915 | return | 
|  | 916 |  | 
|  | 917 | algorithm = OPTIONS.avb_algorithms.get(partition) | 
|  | 918 | assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,) | 
|  | 919 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 920 | print('Replacing AVB signing key for %s with "%s" (%s)' % ( | 
|  | 921 | partition, key, algorithm)) | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 922 | misc_info['avb_' + partition + '_algorithm'] = algorithm | 
|  | 923 | misc_info['avb_' + partition + '_key_path'] = key | 
|  | 924 |  | 
|  | 925 | extra_args = OPTIONS.avb_extra_args.get(partition) | 
|  | 926 | if extra_args: | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 927 | print('Setting extra AVB signing args for %s to "%s"' % ( | 
|  | 928 | partition, extra_args)) | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 929 | args_key = AVB_FOOTER_ARGS_BY_PARTITION[partition] | 
|  | 930 | misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args) | 
|  | 931 |  | 
|  | 932 | for partition in AVB_FOOTER_ARGS_BY_PARTITION: | 
|  | 933 | ReplaceAvbPartitionSigningKey(partition) | 
|  | 934 |  | 
|  | 935 |  | 
| Tao Bao | 19b02fe | 2019-10-09 00:04:28 -0700 | [diff] [blame] | 936 | def RewriteAvbProps(misc_info): | 
|  | 937 | """Rewrites the props in AVB signing args.""" | 
|  | 938 | for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items(): | 
|  | 939 | args = misc_info.get(args_key) | 
|  | 940 | if not args: | 
|  | 941 | continue | 
|  | 942 |  | 
|  | 943 | tokens = [] | 
|  | 944 | changed = False | 
|  | 945 | for token in args.split(' '): | 
|  | 946 | fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition) | 
|  | 947 | if not token.startswith(fingerprint_key): | 
|  | 948 | tokens.append(token) | 
|  | 949 | continue | 
|  | 950 | prefix, tag = token.rsplit('/', 1) | 
|  | 951 | tokens.append('{}/{}'.format(prefix, EditTags(tag))) | 
|  | 952 | changed = True | 
|  | 953 |  | 
|  | 954 | if changed: | 
|  | 955 | result = ' '.join(tokens) | 
|  | 956 | print('Rewriting AVB prop for {}:\n'.format(partition)) | 
|  | 957 | print('  replace: {}'.format(args)) | 
|  | 958 | print('     with: {}'.format(result)) | 
|  | 959 | misc_info[args_key] = result | 
|  | 960 |  | 
|  | 961 |  | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 962 | def BuildKeyMap(misc_info, key_mapping_options): | 
|  | 963 | for s, d in key_mapping_options: | 
|  | 964 | if s is None:   # -d option | 
|  | 965 | devkey = misc_info.get("default_system_dev_certificate", | 
| Dan Willemsen | 0ab1be6 | 2019-04-09 21:35:37 -0700 | [diff] [blame] | 966 | "build/make/target/product/security/testkey") | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 967 | devkeydir = os.path.dirname(devkey) | 
|  | 968 |  | 
|  | 969 | OPTIONS.key_map.update({ | 
|  | 970 | devkeydir + "/testkey":  d + "/releasekey", | 
|  | 971 | devkeydir + "/devkey":   d + "/releasekey", | 
|  | 972 | devkeydir + "/media":    d + "/media", | 
|  | 973 | devkeydir + "/shared":   d + "/shared", | 
|  | 974 | devkeydir + "/platform": d + "/platform", | 
|  | 975 | }) | 
|  | 976 | else: | 
|  | 977 | OPTIONS.key_map[s] = d | 
|  | 978 |  | 
|  | 979 |  | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 980 | def GetApiLevelAndCodename(input_tf_zip): | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 981 | data = input_tf_zip.read("SYSTEM/build.prop").decode() | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 982 | api_level = None | 
|  | 983 | codename = None | 
|  | 984 | for line in data.split("\n"): | 
|  | 985 | line = line.strip() | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 986 | if line and line[0] != '#' and "=" in line: | 
|  | 987 | key, value = line.split("=", 1) | 
|  | 988 | key = key.strip() | 
|  | 989 | if key == "ro.build.version.sdk": | 
|  | 990 | api_level = int(value.strip()) | 
|  | 991 | elif key == "ro.build.version.codename": | 
|  | 992 | codename = value.strip() | 
|  | 993 |  | 
|  | 994 | if api_level is None: | 
|  | 995 | raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") | 
|  | 996 | if codename is None: | 
|  | 997 | raise ValueError("No ro.build.version.codename in SYSTEM/build.prop") | 
|  | 998 |  | 
|  | 999 | return (api_level, codename) | 
|  | 1000 |  | 
|  | 1001 |  | 
|  | 1002 | def GetCodenameToApiLevelMap(input_tf_zip): | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 1003 | data = input_tf_zip.read("SYSTEM/build.prop").decode() | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 1004 | api_level = None | 
|  | 1005 | codenames = None | 
|  | 1006 | for line in data.split("\n"): | 
|  | 1007 | line = line.strip() | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 1008 | if line and line[0] != '#' and "=" in line: | 
|  | 1009 | key, value = line.split("=", 1) | 
|  | 1010 | key = key.strip() | 
|  | 1011 | if key == "ro.build.version.sdk": | 
|  | 1012 | api_level = int(value.strip()) | 
|  | 1013 | elif key == "ro.build.version.all_codenames": | 
|  | 1014 | codenames = value.strip().split(",") | 
|  | 1015 |  | 
|  | 1016 | if api_level is None: | 
|  | 1017 | raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop") | 
|  | 1018 | if codenames is None: | 
|  | 1019 | raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop") | 
|  | 1020 |  | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 1021 | result = {} | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 1022 | for codename in codenames: | 
|  | 1023 | codename = codename.strip() | 
| Tao Bao | badceb2 | 2019-03-15 09:33:43 -0700 | [diff] [blame] | 1024 | if codename: | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 1025 | result[codename] = api_level | 
|  | 1026 | return result | 
|  | 1027 |  | 
|  | 1028 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1029 | def ReadApexKeysInfo(tf_zip): | 
|  | 1030 | """Parses the APEX keys info from a given target-files zip. | 
|  | 1031 |  | 
|  | 1032 | Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a | 
|  | 1033 | dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a | 
|  | 1034 | tuple of (payload_key, container_key). | 
|  | 1035 |  | 
|  | 1036 | Args: | 
|  | 1037 | tf_zip: The input target_files ZipFile (already open). | 
|  | 1038 |  | 
|  | 1039 | Returns: | 
|  | 1040 | (payload_key, container_key): payload_key contains the path to the payload | 
|  | 1041 | signing key; container_key contains the path to the container signing | 
|  | 1042 | key. | 
|  | 1043 | """ | 
|  | 1044 | keys = {} | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 1045 | for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'): | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1046 | line = line.strip() | 
|  | 1047 | if not line: | 
|  | 1048 | continue | 
|  | 1049 | matches = re.match( | 
|  | 1050 | r'^name="(?P<NAME>.*)"\s+' | 
|  | 1051 | r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+' | 
|  | 1052 | r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+' | 
|  | 1053 | r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+' | 
|  | 1054 | r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*)"$', | 
|  | 1055 | line) | 
|  | 1056 | if not matches: | 
|  | 1057 | continue | 
|  | 1058 |  | 
|  | 1059 | name = matches.group('NAME') | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1060 | payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY") | 
|  | 1061 |  | 
|  | 1062 | def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix): | 
|  | 1063 | pubkey_suffix_len = len(pubkey_suffix) | 
|  | 1064 | privkey_suffix_len = len(privkey_suffix) | 
|  | 1065 | return (pubkey.endswith(pubkey_suffix) and | 
|  | 1066 | privkey.endswith(privkey_suffix) and | 
|  | 1067 | pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len]) | 
|  | 1068 |  | 
| Tao Bao | 6d9e3da | 2019-03-26 12:59:25 -0700 | [diff] [blame] | 1069 | # Sanity check on the container key names, as we'll carry them without the | 
|  | 1070 | # extensions. This doesn't apply to payload keys though, which we will use | 
|  | 1071 | # full names only. | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1072 | container_cert = matches.group("CONTAINER_CERT") | 
|  | 1073 | container_private_key = matches.group("CONTAINER_PRIVATE_KEY") | 
| Tao Bao | f454c3a | 2019-04-24 23:53:42 -0700 | [diff] [blame] | 1074 | if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED': | 
|  | 1075 | container_key = 'PRESIGNED' | 
|  | 1076 | elif CompareKeys( | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1077 | container_cert, OPTIONS.public_key_suffix, | 
|  | 1078 | container_private_key, OPTIONS.private_key_suffix): | 
| Tao Bao | f454c3a | 2019-04-24 23:53:42 -0700 | [diff] [blame] | 1079 | container_key = container_cert[:-len(OPTIONS.public_key_suffix)] | 
|  | 1080 | else: | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1081 | raise ValueError("Failed to parse container keys: \n{}".format(line)) | 
|  | 1082 |  | 
| Tao Bao | f454c3a | 2019-04-24 23:53:42 -0700 | [diff] [blame] | 1083 | keys[name] = (payload_private_key, container_key) | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1084 |  | 
|  | 1085 | return keys | 
|  | 1086 |  | 
|  | 1087 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1088 | def main(argv): | 
|  | 1089 |  | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 1090 | key_mapping_options = [] | 
|  | 1091 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1092 | def option_handler(o, a): | 
| Doug Zongker | 05d3dea | 2009-06-22 11:32:31 -0700 | [diff] [blame] | 1093 | if o in ("-e", "--extra_apks"): | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1094 | names, key = a.split("=") | 
|  | 1095 | names = names.split(",") | 
|  | 1096 | for n in names: | 
|  | 1097 | OPTIONS.extra_apks[n] = key | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1098 | elif o == "--extra_apex_payload_key": | 
|  | 1099 | apex_name, key = a.split("=") | 
|  | 1100 | OPTIONS.extra_apex_payload_keys[apex_name] = key | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 1101 | elif o == "--skip_apks_with_path_prefix": | 
|  | 1102 | # Sanity check the prefix, which must be in all upper case. | 
|  | 1103 | prefix = a.split('/')[0] | 
|  | 1104 | if not prefix or prefix != prefix.upper(): | 
|  | 1105 | raise ValueError("Invalid path prefix '%s'" % (a,)) | 
|  | 1106 | OPTIONS.skip_apks_with_path_prefix.add(a) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1107 | elif o in ("-d", "--default_key_mappings"): | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 1108 | key_mapping_options.append((None, a)) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1109 | elif o in ("-k", "--key_mapping"): | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 1110 | key_mapping_options.append(a.split("=", 1)) | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 1111 | elif o in ("-o", "--replace_ota_keys"): | 
|  | 1112 | OPTIONS.replace_ota_keys = True | 
| Doug Zongker | ae87701 | 2009-04-21 10:04:51 -0700 | [diff] [blame] | 1113 | elif o in ("-t", "--tag_changes"): | 
|  | 1114 | new = [] | 
|  | 1115 | for i in a.split(","): | 
|  | 1116 | i = i.strip() | 
|  | 1117 | if not i or i[0] not in "-+": | 
|  | 1118 | raise ValueError("Bad tag change '%s'" % (i,)) | 
|  | 1119 | new.append(i[0] + i[1:].strip()) | 
|  | 1120 | OPTIONS.tag_changes = tuple(new) | 
| Geremy Condra | f19b365 | 2014-07-29 17:54:54 -0700 | [diff] [blame] | 1121 | elif o == "--replace_verity_public_key": | 
|  | 1122 | OPTIONS.replace_verity_public_key = (True, a) | 
|  | 1123 | elif o == "--replace_verity_private_key": | 
|  | 1124 | OPTIONS.replace_verity_private_key = (True, a) | 
| Badhri Jagan Sridharan | 35c9b12 | 2016-06-16 19:58:44 -0700 | [diff] [blame] | 1125 | elif o == "--replace_verity_keyid": | 
|  | 1126 | OPTIONS.replace_verity_keyid = (True, a) | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 1127 | elif o == "--avb_vbmeta_key": | 
|  | 1128 | OPTIONS.avb_keys['vbmeta'] = a | 
|  | 1129 | elif o == "--avb_vbmeta_algorithm": | 
|  | 1130 | OPTIONS.avb_algorithms['vbmeta'] = a | 
|  | 1131 | elif o == "--avb_vbmeta_extra_args": | 
|  | 1132 | OPTIONS.avb_extra_args['vbmeta'] = a | 
|  | 1133 | elif o == "--avb_boot_key": | 
|  | 1134 | OPTIONS.avb_keys['boot'] = a | 
|  | 1135 | elif o == "--avb_boot_algorithm": | 
|  | 1136 | OPTIONS.avb_algorithms['boot'] = a | 
|  | 1137 | elif o == "--avb_boot_extra_args": | 
|  | 1138 | OPTIONS.avb_extra_args['boot'] = a | 
|  | 1139 | elif o == "--avb_dtbo_key": | 
|  | 1140 | OPTIONS.avb_keys['dtbo'] = a | 
|  | 1141 | elif o == "--avb_dtbo_algorithm": | 
|  | 1142 | OPTIONS.avb_algorithms['dtbo'] = a | 
|  | 1143 | elif o == "--avb_dtbo_extra_args": | 
|  | 1144 | OPTIONS.avb_extra_args['dtbo'] = a | 
|  | 1145 | elif o == "--avb_system_key": | 
|  | 1146 | OPTIONS.avb_keys['system'] = a | 
|  | 1147 | elif o == "--avb_system_algorithm": | 
|  | 1148 | OPTIONS.avb_algorithms['system'] = a | 
|  | 1149 | elif o == "--avb_system_extra_args": | 
|  | 1150 | OPTIONS.avb_extra_args['system'] = a | 
| Bowgo Tsai | e4544b1 | 2019-02-27 10:15:51 +0800 | [diff] [blame] | 1151 | elif o == "--avb_system_other_key": | 
|  | 1152 | OPTIONS.avb_keys['system_other'] = a | 
|  | 1153 | elif o == "--avb_system_other_algorithm": | 
|  | 1154 | OPTIONS.avb_algorithms['system_other'] = a | 
|  | 1155 | elif o == "--avb_system_other_extra_args": | 
|  | 1156 | OPTIONS.avb_extra_args['system_other'] = a | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 1157 | elif o == "--avb_vendor_key": | 
|  | 1158 | OPTIONS.avb_keys['vendor'] = a | 
|  | 1159 | elif o == "--avb_vendor_algorithm": | 
|  | 1160 | OPTIONS.avb_algorithms['vendor'] = a | 
|  | 1161 | elif o == "--avb_vendor_extra_args": | 
|  | 1162 | OPTIONS.avb_extra_args['vendor'] = a | 
| Tao Bao | d6085d6 | 2019-05-06 12:55:42 -0700 | [diff] [blame] | 1163 | elif o == "--avb_vbmeta_system_key": | 
|  | 1164 | OPTIONS.avb_keys['vbmeta_system'] = a | 
|  | 1165 | elif o == "--avb_vbmeta_system_algorithm": | 
|  | 1166 | OPTIONS.avb_algorithms['vbmeta_system'] = a | 
|  | 1167 | elif o == "--avb_vbmeta_system_extra_args": | 
|  | 1168 | OPTIONS.avb_extra_args['vbmeta_system'] = a | 
|  | 1169 | elif o == "--avb_vbmeta_vendor_key": | 
|  | 1170 | OPTIONS.avb_keys['vbmeta_vendor'] = a | 
|  | 1171 | elif o == "--avb_vbmeta_vendor_algorithm": | 
|  | 1172 | OPTIONS.avb_algorithms['vbmeta_vendor'] = a | 
|  | 1173 | elif o == "--avb_vbmeta_vendor_extra_args": | 
|  | 1174 | OPTIONS.avb_extra_args['vbmeta_vendor'] = a | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1175 | elif o == "--avb_apex_extra_args": | 
|  | 1176 | OPTIONS.avb_extra_args['apex'] = a | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1177 | else: | 
|  | 1178 | return False | 
|  | 1179 | return True | 
|  | 1180 |  | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 1181 | args = common.ParseOptions( | 
|  | 1182 | argv, __doc__, | 
|  | 1183 | extra_opts="e:d:k:ot:", | 
|  | 1184 | extra_long_opts=[ | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 1185 | "extra_apks=", | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1186 | "extra_apex_payload_key=", | 
| Tao Bao | 93c2a01 | 2018-06-19 12:19:35 -0700 | [diff] [blame] | 1187 | "skip_apks_with_path_prefix=", | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 1188 | "default_key_mappings=", | 
|  | 1189 | "key_mapping=", | 
|  | 1190 | "replace_ota_keys", | 
|  | 1191 | "tag_changes=", | 
|  | 1192 | "replace_verity_public_key=", | 
|  | 1193 | "replace_verity_private_key=", | 
|  | 1194 | "replace_verity_keyid=", | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1195 | "avb_apex_extra_args=", | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 1196 | "avb_vbmeta_algorithm=", | 
|  | 1197 | "avb_vbmeta_key=", | 
|  | 1198 | "avb_vbmeta_extra_args=", | 
|  | 1199 | "avb_boot_algorithm=", | 
|  | 1200 | "avb_boot_key=", | 
|  | 1201 | "avb_boot_extra_args=", | 
|  | 1202 | "avb_dtbo_algorithm=", | 
|  | 1203 | "avb_dtbo_key=", | 
|  | 1204 | "avb_dtbo_extra_args=", | 
|  | 1205 | "avb_system_algorithm=", | 
|  | 1206 | "avb_system_key=", | 
|  | 1207 | "avb_system_extra_args=", | 
| Bowgo Tsai | e4544b1 | 2019-02-27 10:15:51 +0800 | [diff] [blame] | 1208 | "avb_system_other_algorithm=", | 
|  | 1209 | "avb_system_other_key=", | 
|  | 1210 | "avb_system_other_extra_args=", | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 1211 | "avb_vendor_algorithm=", | 
|  | 1212 | "avb_vendor_key=", | 
|  | 1213 | "avb_vendor_extra_args=", | 
| Tao Bao | d6085d6 | 2019-05-06 12:55:42 -0700 | [diff] [blame] | 1214 | "avb_vbmeta_system_algorithm=", | 
|  | 1215 | "avb_vbmeta_system_key=", | 
|  | 1216 | "avb_vbmeta_system_extra_args=", | 
|  | 1217 | "avb_vbmeta_vendor_algorithm=", | 
|  | 1218 | "avb_vbmeta_vendor_key=", | 
|  | 1219 | "avb_vbmeta_vendor_extra_args=", | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 1220 | ], | 
|  | 1221 | extra_option_handler=option_handler) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1222 |  | 
|  | 1223 | if len(args) != 2: | 
|  | 1224 | common.Usage(__doc__) | 
|  | 1225 | sys.exit(1) | 
|  | 1226 |  | 
| Tao Bao | badceb2 | 2019-03-15 09:33:43 -0700 | [diff] [blame] | 1227 | common.InitLogging() | 
|  | 1228 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1229 | input_zip = zipfile.ZipFile(args[0], "r") | 
| Tao Bao | 2b8f489 | 2017-06-13 12:54:58 -0700 | [diff] [blame] | 1230 | output_zip = zipfile.ZipFile(args[1], "w", | 
|  | 1231 | compression=zipfile.ZIP_DEFLATED, | 
|  | 1232 | allowZip64=True) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1233 |  | 
| Doug Zongker | 831840e | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 1234 | misc_info = common.LoadInfoDict(input_zip) | 
|  | 1235 |  | 
|  | 1236 | BuildKeyMap(misc_info, key_mapping_options) | 
|  | 1237 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1238 | apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip) | 
|  | 1239 | apk_keys = GetApkCerts(apk_keys_info) | 
| Doug Zongker | eb338ef | 2009-05-20 16:50:49 -0700 | [diff] [blame] | 1240 |  | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1241 | apex_keys_info = ReadApexKeysInfo(input_zip) | 
|  | 1242 | apex_keys = GetApexKeys(apex_keys_info, apk_keys) | 
|  | 1243 |  | 
|  | 1244 | CheckApkAndApexKeysAvailable( | 
|  | 1245 | input_zip, | 
|  | 1246 | set(apk_keys.keys()) | set(apex_keys.keys()), | 
| Tao Bao | e134399 | 2019-03-19 12:24:03 -0700 | [diff] [blame] | 1247 | compressed_extension, | 
|  | 1248 | apex_keys) | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1249 |  | 
|  | 1250 | key_passwords = common.GetKeyPasswords( | 
|  | 1251 | set(apk_keys.values()) | set(itertools.chain(*apex_keys.values()))) | 
| Tao Bao | 9aa4b9b | 2016-09-29 17:53:56 -0700 | [diff] [blame] | 1252 | platform_api_level, _ = GetApiLevelAndCodename(input_zip) | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 1253 | codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip) | 
| Alex Klyubin | 2cfd1d1 | 2016-01-13 10:32:47 -0800 | [diff] [blame] | 1254 |  | 
| Doug Zongker | 412c02f | 2014-02-13 10:58:24 -0800 | [diff] [blame] | 1255 | ProcessTargetFiles(input_zip, output_zip, misc_info, | 
| Tao Bao | aa7e993 | 2019-03-15 09:37:01 -0700 | [diff] [blame] | 1256 | apk_keys, apex_keys, key_passwords, | 
|  | 1257 | platform_api_level, codename_to_api_level_map, | 
| Narayan Kamath | a07bf04 | 2017-08-14 14:49:21 +0100 | [diff] [blame] | 1258 | compressed_extension) | 
| Doug Zongker | 8e931bf | 2009-04-06 15:21:45 -0700 | [diff] [blame] | 1259 |  | 
| Tao Bao | 2ed665a | 2015-04-01 11:21:55 -0700 | [diff] [blame] | 1260 | common.ZipClose(input_zip) | 
|  | 1261 | common.ZipClose(output_zip) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1262 |  | 
| Tianjie Xu | b48589a | 2016-08-03 19:21:52 -0700 | [diff] [blame] | 1263 | # Skip building userdata.img and cache.img when signing the target files. | 
| Tianjie Xu | 616fbeb | 2017-05-23 14:51:02 -0700 | [diff] [blame] | 1264 | new_args = ["--is_signing"] | 
|  | 1265 | # add_img_to_target_files builds the system image from scratch, so the | 
|  | 1266 | # recovery patch is guaranteed to be regenerated there. | 
|  | 1267 | if OPTIONS.rebuild_recovery: | 
|  | 1268 | new_args.append("--rebuild_recovery") | 
|  | 1269 | new_args.append(args[1]) | 
| Tianjie Xu | b48589a | 2016-08-03 19:21:52 -0700 | [diff] [blame] | 1270 | add_img_to_target_files.main(new_args) | 
| Doug Zongker | 3c84f56 | 2014-07-31 11:06:30 -0700 | [diff] [blame] | 1271 |  | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 1272 | print("done.") | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1273 |  | 
|  | 1274 |  | 
|  | 1275 | if __name__ == '__main__': | 
|  | 1276 | try: | 
|  | 1277 | main(sys.argv[1:]) | 
| Tao Bao | 0c28d2d | 2017-12-24 10:37:38 -0800 | [diff] [blame] | 1278 | except common.ExternalError as e: | 
|  | 1279 | print("\n   ERROR: %s\n" % (e,)) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1280 | sys.exit(1) | 
| Tao Bao | 639118f | 2017-06-19 15:48:02 -0700 | [diff] [blame] | 1281 | finally: | 
|  | 1282 | common.Cleanup() |