• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""
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
23  -e  (--extra_apks)  <name,name,...=key>
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,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.
35
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
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
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
59      META/misc_info.txt.  (Defaulting to "build/make/target/product/security"
60      if the value is not present in misc_info.
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.
64
65  -o  (--replace_ota_keys)
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.
72
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.
78      Default value is "-test-keys,-dev-keys,+release-keys".
79
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. It expects the key
87      filename WITH the extension (e.g. verity_key.pub).
88
89  --replace_verity_keyid <path_to_X509_PEM_cert_file>
90      Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
91      with keyid of the cert pointed by <path_to_X509_PEM_cert_file>.
92
93  --remove_avb_public_keys <key1>,<key2>,...
94      Remove AVB public keys from the first-stage ramdisk. The key file to
95      remove is located at either of the following dirs:
96        - BOOT/RAMDISK/avb/ or
97        - BOOT/RAMDISK/first_stage_ramdisk/avb/
98      The second dir will be used for lookup if BOARD_USES_RECOVERY_AS_BOOT is
99      set to true.
100
101  --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,
102         vbmeta_system,vbmeta_vendor}_algorithm <algorithm>
103  --avb_{boot,init_boot,recovery,system,system_other,vendor,dtbo,vbmeta,
104         vbmeta_system,vbmeta_vendor}_key <key>
105      Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
106      the specified image. Otherwise it uses the existing values in info dict.
107
108  --avb_{apex,init_boot,boot,recovery,system,system_other,vendor,dtbo,vbmeta,
109         vbmeta_system,vbmeta_vendor}_extra_args <args>
110      Specify any additional args that are needed to AVB-sign the image
111      (e.g. "--signing_helper /path/to/helper"). The args will be appended to
112      the existing ones in info dict.
113
114  --avb_extra_custom_image_key <partition=key>
115  --avb_extra_custom_image_algorithm <partition=algorithm>
116      Use the specified algorithm (e.g. SHA256_RSA4096) and the key to AVB-sign
117      the specified custom images mounted on the partition. Otherwise it uses
118      the existing values in info dict.
119
120  --avb_extra_custom_image_extra_args <partition=extra_args>
121      Specify any additional args that are needed to AVB-sign the custom images
122      mounted on the partition (e.g. "--signing_helper /path/to/helper"). The
123      args will be appended to the existing ones in info dict.
124
125  --gki_signing_algorithm <algorithm>
126  --gki_signing_key <key>
127  --gki_signing_extra_args <args>
128      DEPRECATED Does nothing.
129
130  --android_jar_path <path>
131      Path to the android.jar to repack the apex file.
132
133  --allow_gsi_debug_sepolicy
134      Allow the existence of the file 'userdebug_plat_sepolicy.cil' under
135      (/system/system_ext|/system_ext)/etc/selinux.
136      If not set, error out when the file exists.
137
138  --override_apk_keys <path>
139      Replace all APK keys with this private key
140
141  --override_apex_keys <path>
142      Replace all APEX keys with this private key
143
144  -k  (--package_key) <key>
145      Key to use to sign the package (default is the value of
146      default_system_dev_certificate from the input target-files's
147      META/misc_info.txt, or "build/make/target/product/security/testkey" if
148      that value is not specified).
149
150      For incremental OTAs, the default value is based on the source
151      target-file, not the target build.
152
153  --payload_signer <signer>
154      Specify the signer when signing the payload and metadata for A/B OTAs.
155      By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
156      with the package private key. If the private key cannot be accessed
157      directly, a payload signer that knows how to do that should be specified.
158      The signer will be supplied with "-inkey <path_to_key>",
159      "-in <input_file>" and "-out <output_file>" parameters.
160
161  --payload_signer_args <args>
162      Specify the arguments needed for payload signer.
163
164  --payload_signer_maximum_signature_size <signature_size>
165      The maximum signature size (in bytes) that would be generated by the given
166      payload signer. Only meaningful when custom payload signer is specified
167      via '--payload_signer'.
168      If the signer uses a RSA key, this should be the number of bytes to
169      represent the modulus. If it uses an EC key, this is the size of a
170      DER-encoded ECDSA signature.
171"""
172
173from __future__ import print_function
174
175import base64
176import copy
177import errno
178import gzip
179import io
180import itertools
181import logging
182import os
183import re
184import shutil
185import stat
186import sys
187import shlex
188import tempfile
189import zipfile
190from xml.etree import ElementTree
191
192import add_img_to_target_files
193import ota_from_raw_img
194import apex_utils
195import common
196import payload_signer
197import update_payload
198from payload_signer import SignOtaPackage, PAYLOAD_BIN
199
200
201if sys.hexversion < 0x02070000:
202  print("Python 2.7 or newer is required.", file=sys.stderr)
203  sys.exit(1)
204
205
206logger = logging.getLogger(__name__)
207
208OPTIONS = common.OPTIONS
209
210OPTIONS.extra_apks = {}
211OPTIONS.extra_apex_payload_keys = {}
212OPTIONS.skip_apks_with_path_prefix = set()
213OPTIONS.key_map = {}
214OPTIONS.rebuild_recovery = False
215OPTIONS.replace_ota_keys = False
216OPTIONS.remove_avb_public_keys = None
217OPTIONS.tag_changes = ("-test-keys", "-dev-keys", "+release-keys")
218OPTIONS.avb_keys = {}
219OPTIONS.avb_algorithms = {}
220OPTIONS.avb_extra_args = {}
221OPTIONS.android_jar_path = None
222OPTIONS.vendor_partitions = set()
223OPTIONS.vendor_otatools = None
224OPTIONS.allow_gsi_debug_sepolicy = False
225OPTIONS.override_apk_keys = None
226OPTIONS.override_apex_keys = None
227OPTIONS.input_tmp = None
228
229
230AVB_FOOTER_ARGS_BY_PARTITION = {
231    'boot': 'avb_boot_add_hash_footer_args',
232    'init_boot': 'avb_init_boot_add_hash_footer_args',
233    'dtbo': 'avb_dtbo_add_hash_footer_args',
234    'product': 'avb_product_add_hashtree_footer_args',
235    'recovery': 'avb_recovery_add_hash_footer_args',
236    'system': 'avb_system_add_hashtree_footer_args',
237    'system_dlkm': "avb_system_dlkm_add_hashtree_footer_args",
238    'system_ext': 'avb_system_ext_add_hashtree_footer_args',
239    'system_other': 'avb_system_other_add_hashtree_footer_args',
240    'odm': 'avb_odm_add_hashtree_footer_args',
241    'odm_dlkm': 'avb_odm_dlkm_add_hashtree_footer_args',
242    'pvmfw': 'avb_pvmfw_add_hash_footer_args',
243    'vendor': 'avb_vendor_add_hashtree_footer_args',
244    'vendor_boot': 'avb_vendor_boot_add_hash_footer_args',
245    'vendor_kernel_boot': 'avb_vendor_kernel_boot_add_hash_footer_args',
246    'vendor_dlkm': "avb_vendor_dlkm_add_hashtree_footer_args",
247    'vbmeta': 'avb_vbmeta_args',
248    'vbmeta_system': 'avb_vbmeta_system_args',
249    'vbmeta_vendor': 'avb_vbmeta_vendor_args',
250}
251
252
253# Check that AVB_FOOTER_ARGS_BY_PARTITION is in sync with AVB_PARTITIONS.
254for partition in common.AVB_PARTITIONS:
255  if partition not in AVB_FOOTER_ARGS_BY_PARTITION:
256    raise RuntimeError("Missing {} in AVB_FOOTER_ARGS".format(partition))
257
258# Partitions that can be regenerated after signing using a separate
259# vendor otatools package.
260ALLOWED_VENDOR_PARTITIONS = set(["vendor", "odm"])
261
262
263def IsApexFile(filename):
264  return filename.endswith(".apex") or filename.endswith(".capex")
265
266
267def IsOtaPackage(fp):
268  with zipfile.ZipFile(fp) as zfp:
269    if not PAYLOAD_BIN in zfp.namelist():
270      return False
271    with zfp.open(PAYLOAD_BIN, "r") as payload:
272      magic = payload.read(4)
273      return magic == b"CrAU"
274
275
276def IsEntryOtaPackage(input_zip, filename):
277  with input_zip.open(filename, "r") as fp:
278    external_attr = input_zip.getinfo(filename).external_attr
279    if stat.S_ISLNK(external_attr >> 16):
280      return IsEntryOtaPackage(input_zip,
281          os.path.join(os.path.dirname(filename), fp.read().decode()))
282    return IsOtaPackage(fp)
283
284
285def GetApexFilename(filename):
286  name = os.path.basename(filename)
287  # Replace the suffix for compressed apex
288  if name.endswith(".capex"):
289    return name.replace(".capex", ".apex")
290  return name
291
292
293def GetApkCerts(certmap):
294  if OPTIONS.override_apk_keys is not None:
295    for apk in certmap.keys():
296      certmap[apk] = OPTIONS.override_apk_keys
297
298  # apply the key remapping to the contents of the file
299  for apk, cert in certmap.items():
300    certmap[apk] = OPTIONS.key_map.get(cert, cert)
301
302  # apply all the -e options, overriding anything in the file
303  for apk, cert in OPTIONS.extra_apks.items():
304    if not cert:
305      cert = "PRESIGNED"
306    certmap[apk] = OPTIONS.key_map.get(cert, cert)
307
308  return certmap
309
310
311def GetApexKeys(keys_info, key_map):
312  """Gets APEX payload and container signing keys by applying the mapping rules.
313
314  Presigned payload / container keys will be set accordingly.
315
316  Args:
317    keys_info: A dict that maps from APEX filenames to a tuple of (payload_key,
318        container_key, sign_tool).
319    key_map: A dict that overrides the keys, specified via command-line input.
320
321  Returns:
322    A dict that contains the updated APEX key mapping, which should be used for
323    the current signing.
324
325  Raises:
326    AssertionError: On invalid container / payload key overrides.
327  """
328  if OPTIONS.override_apex_keys is not None:
329    for apex in keys_info.keys():
330      keys_info[apex] = (OPTIONS.override_apex_keys, keys_info[apex][1], keys_info[apex][2])
331
332  if OPTIONS.override_apk_keys is not None:
333    key = key_map.get(OPTIONS.override_apk_keys, OPTIONS.override_apk_keys)
334    for apex in keys_info.keys():
335      keys_info[apex] = (keys_info[apex][0], key, keys_info[apex][2])
336
337  # Apply all the --extra_apex_payload_key options to override the payload
338  # signing keys in the given keys_info.
339  for apex, key in OPTIONS.extra_apex_payload_keys.items():
340    if not key:
341      key = 'PRESIGNED'
342    if apex not in keys_info:
343      logger.warning('Failed to find %s in target_files; Ignored', apex)
344      continue
345    keys_info[apex] = (key, keys_info[apex][1], keys_info[apex][2])
346
347  # Apply the key remapping to container keys.
348  for apex, (payload_key, container_key, sign_tool) in keys_info.items():
349    keys_info[apex] = (payload_key, key_map.get(container_key, container_key), sign_tool)
350
351  # Apply all the --extra_apks options to override the container keys.
352  for apex, key in OPTIONS.extra_apks.items():
353    # Skip non-APEX containers.
354    if apex not in keys_info:
355      continue
356    if not key:
357      key = 'PRESIGNED'
358    keys_info[apex] = (keys_info[apex][0], key_map.get(key, key), keys_info[apex][2])
359
360  # A PRESIGNED container entails a PRESIGNED payload. Apply this to all the
361  # APEX key pairs. However, a PRESIGNED container with non-PRESIGNED payload
362  # (overridden via commandline) indicates a config error, which should not be
363  # allowed.
364  for apex, (payload_key, container_key, sign_tool) in keys_info.items():
365    if container_key != 'PRESIGNED':
366      continue
367    if apex in OPTIONS.extra_apex_payload_keys:
368      payload_override = OPTIONS.extra_apex_payload_keys[apex]
369      assert payload_override == '', \
370          ("Invalid APEX key overrides: {} has PRESIGNED container but "
371           "non-PRESIGNED payload key {}").format(apex, payload_override)
372    if payload_key != 'PRESIGNED':
373      print(
374          "Setting {} payload as PRESIGNED due to PRESIGNED container".format(
375              apex))
376    keys_info[apex] = ('PRESIGNED', 'PRESIGNED', None)
377
378  return keys_info
379
380
381def GetMicrodroidVbmetaKey(virt_apex_path, avbtool_path):
382  """Extracts the AVB public key from microdroid_vbmeta.img within a virt apex.
383
384  Args:
385    virt_apex_path: The path to the com.android.virt.apex file.
386    avbtool_path: The path to the avbtool executable.
387
388  Returns:
389    The AVB public key (bytes).
390  """
391  # Creates an ApexApkSigner to extract microdroid_vbmeta.img.
392  # No need to set key_passwords/codename_to_api_level_map since
393  # we won't do signing here.
394  apex_signer = apex_utils.ApexApkSigner(
395      virt_apex_path,
396      None,  # key_passwords
397      None)  # codename_to_api_level_map
398  payload_dir = apex_signer.ExtractApexPayload(virt_apex_path)
399  microdroid_vbmeta_image = os.path.join(
400      payload_dir, 'etc', 'fs', 'microdroid_vbmeta.img')
401
402  # Extracts the avb public key from microdroid_vbmeta.img.
403  with tempfile.NamedTemporaryFile() as microdroid_pubkey:
404    common.RunAndCheckOutput([
405        avbtool_path, 'info_image',
406        '--image', microdroid_vbmeta_image,
407        '--output_pubkey', microdroid_pubkey.name])
408    with open(microdroid_pubkey.name, 'rb') as f:
409      return f.read()
410
411
412def GetApkFileInfo(filename, compressed_extension, skipped_prefixes):
413  """Returns the APK info based on the given filename.
414
415  Checks if the given filename (with path) looks like an APK file, by taking the
416  compressed extension into consideration. If it appears to be an APK file,
417  further checks if the APK file should be skipped when signing, based on the
418  given path prefixes.
419
420  Args:
421    filename: Path to the file.
422    compressed_extension: The extension string of compressed APKs (e.g. ".gz"),
423        or None if there's no compressed APKs.
424    skipped_prefixes: A set/list/tuple of the path prefixes to be skipped.
425
426  Returns:
427    (is_apk, is_compressed, should_be_skipped): is_apk indicates whether the
428    given filename is an APK file. is_compressed indicates whether the APK file
429    is compressed (only meaningful when is_apk is True). should_be_skipped
430    indicates whether the filename matches any of the given prefixes to be
431    skipped.
432
433  Raises:
434    AssertionError: On invalid compressed_extension or skipped_prefixes inputs.
435  """
436  assert compressed_extension is None or compressed_extension.startswith('.'), \
437      "Invalid compressed_extension arg: '{}'".format(compressed_extension)
438
439  # skipped_prefixes should be one of set/list/tuple types. Other types such as
440  # str shouldn't be accepted.
441  assert isinstance(skipped_prefixes, (set, list, tuple)), \
442      "Invalid skipped_prefixes input type: {}".format(type(skipped_prefixes))
443
444  compressed_apk_extension = (
445      ".apk" + compressed_extension if compressed_extension else None)
446  is_apk = (filename.endswith(".apk") or
447            (compressed_apk_extension and
448             filename.endswith(compressed_apk_extension)))
449  if not is_apk:
450    return (False, False, False)
451
452  is_compressed = (compressed_apk_extension and
453                   filename.endswith(compressed_apk_extension))
454  should_be_skipped = filename.startswith(tuple(skipped_prefixes))
455  return (True, is_compressed, should_be_skipped)
456
457
458def CheckApkAndApexKeysAvailable(input_tf_zip, known_keys,
459                                 compressed_extension, apex_keys):
460  """Checks that all the APKs and APEXes have keys specified.
461
462  Args:
463    input_tf_zip: An open target_files zip file.
464    known_keys: A set of APKs and APEXes that have known signing keys.
465    compressed_extension: The extension string of compressed APKs, such as
466        '.gz', or None if there's no compressed APKs.
467    apex_keys: A dict that contains the key mapping from APEX name to
468        (payload_key, container_key, sign_tool).
469
470  Raises:
471    AssertionError: On finding unknown APKs and APEXes.
472  """
473  unknown_files = []
474  for info in input_tf_zip.infolist():
475    # Handle APEXes on all partitions
476    if IsApexFile(info.filename):
477      name = GetApexFilename(info.filename)
478      if name not in known_keys:
479        unknown_files.append(name)
480      continue
481
482    # And APKs.
483    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
484        info.filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
485    if not is_apk or should_be_skipped:
486      continue
487
488    name = os.path.basename(info.filename)
489    if is_compressed:
490      name = name[:-len(compressed_extension)]
491    if name not in known_keys:
492      unknown_files.append(name)
493
494  assert not unknown_files, \
495      ("No key specified for:\n  {}\n"
496       "Use '-e <apkname>=' to specify a key (which may be an empty string to "
497       "not sign this apk).".format("\n  ".join(unknown_files)))
498
499  # For all the APEXes, double check that we won't have an APEX that has only
500  # one of the payload / container keys set. Note that non-PRESIGNED container
501  # with PRESIGNED payload could be allowed but currently unsupported. It would
502  # require changing SignApex implementation.
503  if not apex_keys:
504    return
505
506  invalid_apexes = []
507  for info in input_tf_zip.infolist():
508    if not IsApexFile(info.filename):
509      continue
510
511    name = GetApexFilename(info.filename)
512
513    (payload_key, container_key, _) = apex_keys[name]
514    if ((payload_key in common.SPECIAL_CERT_STRINGS and
515         container_key not in common.SPECIAL_CERT_STRINGS) or
516        (payload_key not in common.SPECIAL_CERT_STRINGS and
517         container_key in common.SPECIAL_CERT_STRINGS)):
518      invalid_apexes.append(
519          "{}: payload_key {}, container_key {}".format(
520              name, payload_key, container_key))
521
522  assert not invalid_apexes, \
523      "Invalid APEX keys specified:\n  {}\n".format(
524          "\n  ".join(invalid_apexes))
525
526
527def SignApk(data, keyname, pw, platform_api_level, codename_to_api_level_map,
528            is_compressed, apk_name):
529  unsigned = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
530  unsigned.write(data)
531  unsigned.flush()
532
533  if is_compressed:
534    uncompressed = tempfile.NamedTemporaryFile()
535    with gzip.open(unsigned.name, "rb") as in_file, \
536            open(uncompressed.name, "wb") as out_file:
537      shutil.copyfileobj(in_file, out_file)
538
539    # Finally, close the "unsigned" file (which is gzip compressed), and then
540    # replace it with the uncompressed version.
541    #
542    # TODO(narayan): All this nastiness can be avoided if python 3.2 is in use,
543    # we could just gzip / gunzip in-memory buffers instead.
544    unsigned.close()
545    unsigned = uncompressed
546
547  signed = tempfile.NamedTemporaryFile(suffix='_' + apk_name)
548
549  # For pre-N builds, don't upgrade to SHA-256 JAR signatures based on the APK's
550  # minSdkVersion to avoid increasing incremental OTA update sizes. If an APK
551  # didn't change, we don't want its signature to change due to the switch
552  # from SHA-1 to SHA-256.
553  # By default, APK signer chooses SHA-256 signatures if the APK's minSdkVersion
554  # is 18 or higher. For pre-N builds we disable this mechanism by pretending
555  # that the APK's minSdkVersion is 1.
556  # For N+ builds, we let APK signer rely on the APK's minSdkVersion to
557  # determine whether to use SHA-256.
558  min_api_level = None
559  if platform_api_level > 23:
560    # Let APK signer choose whether to use SHA-1 or SHA-256, based on the APK's
561    # minSdkVersion attribute
562    min_api_level = None
563  else:
564    # Force APK signer to use SHA-1
565    min_api_level = 1
566
567  common.SignFile(unsigned.name, signed.name, keyname, pw,
568                  min_api_level=min_api_level,
569                  codename_to_api_level_map=codename_to_api_level_map)
570
571  data = None
572  if is_compressed:
573    # Recompress the file after it has been signed.
574    compressed = tempfile.NamedTemporaryFile()
575    with open(signed.name, "rb") as in_file, \
576            gzip.open(compressed.name, "wb") as out_file:
577      shutil.copyfileobj(in_file, out_file)
578
579    data = compressed.read()
580    compressed.close()
581  else:
582    data = signed.read()
583
584  unsigned.close()
585  signed.close()
586
587  return data
588
589
590
591def IsBuildPropFile(filename):
592  return filename in (
593      "SYSTEM/etc/prop.default",
594      "BOOT/RAMDISK/prop.default",
595      "RECOVERY/RAMDISK/prop.default",
596
597      "VENDOR_BOOT/RAMDISK/default.prop",
598      "VENDOR_BOOT/RAMDISK/prop.default",
599
600      # ROOT/default.prop is a legacy path, but may still exist for upgrading
601      # devices that don't support `property_overrides_split_enabled`.
602      "ROOT/default.prop",
603
604      # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
605      # as a symlink in the current code. So it's a no-op here. Keeping the
606      # path here for clarity.
607      # Some build props might be stored under path
608      # VENDOR_BOOT/RAMDISK_FRAGMENTS/recovery/RAMDISK/default.prop, and
609      # default.prop can be a symbolic link to prop.default, so overwrite all
610      # files that ends with build.prop, default.prop or prop.default
611      "RECOVERY/RAMDISK/default.prop") or \
612        filename.endswith("build.prop") or \
613        filename.endswith("/default.prop") or \
614        filename.endswith("/prop.default")
615
616
617def GetOtaSigningArgs():
618  args = []
619  if OPTIONS.package_key:
620    args.extend(["--package_key", OPTIONS.package_key])
621  if OPTIONS.payload_signer:
622    args.extend(["--payload_signer=" + OPTIONS.payload_signer])
623  if OPTIONS.payload_signer_args:
624    args.extend(["--payload_signer_args=" + shlex.join(OPTIONS.payload_signer_args)])
625  if OPTIONS.search_path:
626    args.extend(["--search_path", OPTIONS.search_path])
627  if OPTIONS.payload_signer_maximum_signature_size:
628    args.extend(["--payload_signer_maximum_signature_size",
629                OPTIONS.payload_signer_maximum_signature_size])
630  if OPTIONS.private_key_suffix:
631    args.extend(["--private_key_suffix", OPTIONS.private_key_suffix])
632  return args
633
634
635def RegenerateKernelPartitions(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info):
636  """Re-generate boot and dtbo partitions using new signing configuration"""
637  files_to_unzip = [
638      "PREBUILT_IMAGES/*", "BOOTABLE_IMAGES/*.img", "*/boot_16k.img", "*/dtbo_16k.img"]
639  if OPTIONS.input_tmp is None:
640    OPTIONS.input_tmp = common.UnzipTemp(input_tf_zip.filename, files_to_unzip)
641  else:
642    common.UnzipToDir(input_tf_zip.filename, OPTIONS.input_tmp, files_to_unzip)
643  unzip_dir = OPTIONS.input_tmp
644  os.makedirs(os.path.join(unzip_dir, "IMAGES"), exist_ok=True)
645
646  boot_image = common.GetBootableImage(
647      "IMAGES/boot.img", "boot.img", unzip_dir, "BOOT", misc_info)
648  if boot_image:
649    boot_image.WriteToDir(unzip_dir)
650    boot_image = os.path.join(unzip_dir, boot_image.name)
651    common.ZipWrite(output_tf_zip, boot_image, "IMAGES/boot.img",
652                    compress_type=zipfile.ZIP_STORED)
653  if misc_info.get("has_dtbo") == "true":
654    add_img_to_target_files.AddDtbo(output_tf_zip)
655  return unzip_dir
656
657
658def RegenerateBootOTA(input_tf_zip: zipfile.ZipFile, filename, input_ota):
659  with input_tf_zip.open(filename, "r") as in_fp:
660    payload = update_payload.Payload(in_fp)
661  is_incremental = any([part.HasField('old_partition_info')
662                        for part in payload.manifest.partitions])
663  is_boot_ota = filename.startswith(
664      "VENDOR/boot_otas/") or filename.startswith("SYSTEM/boot_otas/")
665  if not is_boot_ota:
666    return
667  is_4k_boot_ota = filename in [
668      "VENDOR/boot_otas/boot_ota_4k.zip", "SYSTEM/boot_otas/boot_ota_4k.zip"]
669  # Only 4K boot image is re-generated, so if 16K boot ota isn't incremental,
670  # we do not need to re-generate
671  if not is_4k_boot_ota and not is_incremental:
672    return
673
674  timestamp = str(payload.manifest.max_timestamp)
675  partitions = [part.partition_name for part in payload.manifest.partitions]
676  unzip_dir = OPTIONS.input_tmp
677  signed_boot_image = os.path.join(unzip_dir, "IMAGES", "boot.img")
678  if not os.path.exists(signed_boot_image):
679    logger.warn("Need to re-generate boot OTA {} but failed to get signed boot image. 16K dev option will be impacted, after rolling back to 4K user would need to sideload/flash their device to continue receiving OTAs.")
680    return
681  signed_dtbo_image = os.path.join(unzip_dir, "IMAGES", "dtbo.img")
682  if "dtbo" in partitions and not os.path.exists(signed_dtbo_image):
683    raise ValueError(
684        "Boot OTA {} has dtbo partition, but no dtbo image found in target files.".format(filename))
685  if is_incremental:
686    signed_16k_boot_image = os.path.join(
687        unzip_dir, "IMAGES", "boot_16k.img")
688    signed_16k_dtbo_image = os.path.join(
689        unzip_dir, "IMAGES", "dtbo_16k.img")
690    if is_4k_boot_ota:
691      if os.path.exists(signed_16k_boot_image):
692        signed_boot_image = signed_16k_boot_image + ":" + signed_boot_image
693      if os.path.exists(signed_16k_dtbo_image):
694        signed_dtbo_image = signed_16k_dtbo_image + ":" + signed_dtbo_image
695    else:
696      if os.path.exists(signed_16k_boot_image):
697        signed_boot_image += ":" + signed_16k_boot_image
698      if os.path.exists(signed_16k_dtbo_image):
699        signed_dtbo_image += ":" + signed_16k_dtbo_image
700
701  args = ["ota_from_raw_img",
702          "--max_timestamp", timestamp, "--output", input_ota.name]
703  args.extend(GetOtaSigningArgs())
704  if "dtbo" in partitions:
705    args.extend(["--partition_name", "boot,dtbo",
706                signed_boot_image, signed_dtbo_image])
707  else:
708    args.extend(["--partition_name", "boot", signed_boot_image])
709  logger.info(
710      "Re-generating boot OTA {} using cmd {}".format(filename, args))
711  ota_from_raw_img.main(args)
712
713
714def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip: zipfile.ZipFile, misc_info,
715                       apk_keys, apex_keys, key_passwords,
716                       platform_api_level, codename_to_api_level_map,
717                       compressed_extension):
718  # maxsize measures the maximum filename length, including the ones to be
719  # skipped.
720  try:
721    maxsize = max(
722        [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
723         if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
724  except ValueError:
725    # Sets this to zero for targets without APK files.
726    maxsize = 0
727
728  # Replace the AVB signing keys, if any.
729  ReplaceAvbSigningKeys(misc_info)
730  OPTIONS.info_dict = misc_info
731
732  # Rewrite the props in AVB signing args.
733  if misc_info.get('avb_enable') == 'true':
734    RewriteAvbProps(misc_info)
735
736  RegenerateKernelPartitions(input_tf_zip, output_tf_zip, misc_info)
737
738  for info in input_tf_zip.infolist():
739    filename = info.filename
740    if filename.startswith("IMAGES/"):
741      continue
742
743    # Skip OTA-specific images (e.g. split super images), which will be
744    # re-generated during signing.
745    if filename.startswith("OTA/") and filename.endswith(".img"):
746      continue
747
748    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
749        filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)
750    data = input_tf_zip.read(filename)
751    out_info = copy.copy(info)
752
753    if is_apk and should_be_skipped:
754      # Copy skipped APKs verbatim.
755      print(
756          "NOT signing: %s\n"
757          "        (skipped due to matching prefix)" % (filename,))
758      common.ZipWriteStr(output_tf_zip, out_info, data)
759
760    # Sign APKs.
761    elif is_apk:
762      name = os.path.basename(filename)
763      if is_compressed:
764        name = name[:-len(compressed_extension)]
765
766      key = apk_keys[name]
767      if key not in common.SPECIAL_CERT_STRINGS:
768        print("    signing: %-*s (%s)" % (maxsize, name, key))
769        signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
770                              codename_to_api_level_map, is_compressed, name)
771        common.ZipWriteStr(output_tf_zip, out_info, signed_data)
772      else:
773        # an APK we're not supposed to sign.
774        print(
775            "NOT signing: %s\n"
776            "        (skipped due to special cert string)" % (name,))
777        common.ZipWriteStr(output_tf_zip, out_info, data)
778
779    # Sign bundled APEX files on all partitions
780    elif IsApexFile(filename):
781      name = GetApexFilename(filename)
782
783      payload_key, container_key, sign_tool = apex_keys[name]
784
785      # We've asserted not having a case with only one of them PRESIGNED.
786      if (payload_key not in common.SPECIAL_CERT_STRINGS and
787              container_key not in common.SPECIAL_CERT_STRINGS):
788        print("    signing: %-*s container (%s)" % (
789            maxsize, name, container_key))
790        print("           : %-*s payload   (%s)" % (
791            maxsize, name, payload_key))
792
793        signed_apex = apex_utils.SignApex(
794            misc_info['avb_avbtool'],
795            data,
796            payload_key,
797            container_key,
798            key_passwords,
799            apk_keys,
800            codename_to_api_level_map,
801            no_hashtree=None,  # Let apex_util determine if hash tree is needed
802            signing_args=OPTIONS.avb_extra_args.get('apex'),
803            sign_tool=sign_tool)
804        common.ZipWrite(output_tf_zip, signed_apex, filename)
805
806      else:
807        print(
808            "NOT signing: %s\n"
809            "        (skipped due to special cert string)" % (name,))
810        common.ZipWriteStr(output_tf_zip, out_info, data)
811
812    elif filename.endswith(".zip") and IsEntryOtaPackage(input_tf_zip, filename):
813      logger.info("Re-signing OTA package {}".format(filename))
814      with tempfile.NamedTemporaryFile() as input_ota, tempfile.NamedTemporaryFile() as output_ota:
815        RegenerateBootOTA(input_tf_zip, filename, input_ota)
816
817        SignOtaPackage(input_ota.name, output_ota.name)
818        common.ZipWrite(output_tf_zip, output_ota.name, filename,
819                        compress_type=zipfile.ZIP_STORED)
820    # System properties.
821    elif IsBuildPropFile(filename):
822      print("Rewriting %s:" % (filename,))
823      if stat.S_ISLNK(info.external_attr >> 16):
824        new_data = data
825      else:
826        new_data = RewriteProps(data.decode())
827      common.ZipWriteStr(output_tf_zip, out_info, new_data)
828
829    # Replace the certs in *mac_permissions.xml (there could be multiple, such
830    # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml).
831    elif filename.endswith("mac_permissions.xml"):
832      print("Rewriting %s with new keys." % (filename,))
833      new_data = ReplaceCerts(data.decode())
834      common.ZipWriteStr(output_tf_zip, out_info, new_data)
835
836    # Ask add_img_to_target_files to rebuild the recovery patch if needed.
837    elif filename in ("SYSTEM/recovery-from-boot.p",
838                      "VENDOR/recovery-from-boot.p",
839
840                      "SYSTEM/etc/recovery.img",
841                      "VENDOR/etc/recovery.img",
842
843                      "SYSTEM/bin/install-recovery.sh",
844                      "VENDOR/bin/install-recovery.sh"):
845      OPTIONS.rebuild_recovery = True
846
847    # Don't copy OTA certs if we're replacing them.
848    # Replacement of update-payload-key.pub.pem was removed in b/116660991.
849    elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
850      pass
851
852    # Skip META/misc_info.txt since we will write back the new values later.
853    elif filename == "META/misc_info.txt":
854      pass
855
856    elif (OPTIONS.remove_avb_public_keys and
857          (filename.startswith("BOOT/RAMDISK/avb/") or
858           filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
859      matched_removal = False
860      for key_to_remove in OPTIONS.remove_avb_public_keys:
861        if filename.endswith(key_to_remove):
862          matched_removal = True
863          print("Removing AVB public key from ramdisk: %s" % filename)
864          break
865      if not matched_removal:
866        # Copy it verbatim if we don't want to remove it.
867        common.ZipWriteStr(output_tf_zip, out_info, data)
868
869    # Skip the vbmeta digest as we will recalculate it.
870    elif filename == "META/vbmeta_digest.txt":
871      pass
872
873    # Skip the care_map as we will regenerate the system/vendor images.
874    elif filename in ["META/care_map.pb", "META/care_map.txt"]:
875      pass
876
877    # Skip apex_info.pb because we sign/modify apexes
878    elif filename == "META/apex_info.pb":
879      pass
880
881    # Updates system_other.avbpubkey in /product/etc/.
882    elif filename in (
883        "PRODUCT/etc/security/avb/system_other.avbpubkey",
884        "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
885      # Only update system_other's public key, if the corresponding signing
886      # key is specified via --avb_system_other_key.
887      signing_key = OPTIONS.avb_keys.get("system_other")
888      if signing_key:
889        public_key = common.ExtractAvbPublicKey(
890            misc_info['avb_avbtool'], signing_key)
891        print("    Rewriting AVB public key of system_other in /product")
892        common.ZipWrite(output_tf_zip, public_key, filename)
893
894    # Updates pvmfw embedded public key with the virt APEX payload key.
895    elif filename == "PREBUILT_IMAGES/pvmfw.img":
896      # Find the path of the virt APEX in the target files.
897      namelist = input_tf_zip.namelist()
898      apex_gen = (f for f in namelist if IsApexFile(f))
899      virt_apex_re = re.compile("^.*com\.([^\.]+\.)?android\.virt\.apex$")
900      virt_apex_path = next(
901        (a for a in apex_gen if virt_apex_re.match(a)), None)
902      if not virt_apex_path:
903        print("Removing %s from ramdisk: virt APEX not found" % filename)
904      else:
905        print("Replacing %s embedded key with %s key" % (filename,
906                                                         virt_apex_path))
907        # Get the current and new embedded keys.
908        virt_apex = GetApexFilename(virt_apex_path)
909        payload_key, container_key, sign_tool = apex_keys[virt_apex]
910
911        # b/384813199: handles the pre-signed com.android.virt.apex in GSI.
912        if payload_key == 'PRESIGNED':
913          with tempfile.NamedTemporaryFile() as virt_apex_temp_file:
914            virt_apex_temp_file.write(input_tf_zip.read(virt_apex_path))
915            virt_apex_temp_file.flush()
916            new_pubkey = GetMicrodroidVbmetaKey(virt_apex_temp_file.name,
917                                                misc_info['avb_avbtool'])
918        else:
919          new_pubkey_path = common.ExtractAvbPublicKey(
920              misc_info['avb_avbtool'], payload_key)
921          with open(new_pubkey_path, 'rb') as f:
922            new_pubkey = f.read()
923
924        pubkey_info = copy.copy(
925            input_tf_zip.getinfo("PREBUILT_IMAGES/pvmfw_embedded.avbpubkey"))
926        old_pubkey = input_tf_zip.read(pubkey_info.filename)
927        # Validate the keys and image.
928        if len(old_pubkey) != len(new_pubkey):
929          raise common.ExternalError("pvmfw embedded public key size mismatch")
930        pos = data.find(old_pubkey)
931        if pos == -1:
932          raise common.ExternalError("pvmfw embedded public key not found")
933        # Replace the key and copy new files.
934        new_data = data[:pos] + new_pubkey + data[pos+len(old_pubkey):]
935        common.ZipWriteStr(output_tf_zip, out_info, new_data)
936        common.ZipWriteStr(output_tf_zip, pubkey_info, new_pubkey)
937    elif filename == "PREBUILT_IMAGES/pvmfw_embedded.avbpubkey":
938      pass
939
940    # Should NOT sign boot-debug.img.
941    elif filename in (
942        "BOOT/RAMDISK/force_debuggable",
943        "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
944      raise common.ExternalError("debuggable boot.img cannot be signed")
945
946    # Should NOT sign userdebug sepolicy file.
947    elif filename in (
948        "SYSTEM_EXT/etc/selinux/userdebug_plat_sepolicy.cil",
949        "SYSTEM/system_ext/etc/selinux/userdebug_plat_sepolicy.cil"):
950      if not OPTIONS.allow_gsi_debug_sepolicy:
951        raise common.ExternalError("debug sepolicy shouldn't be included")
952      else:
953        # Copy it verbatim if we allow the file to exist.
954        common.ZipWriteStr(output_tf_zip, out_info, data)
955
956    # Sign microdroid_vendor.img.
957    elif filename == "VENDOR/etc/avf/microdroid/microdroid_vendor.img":
958      vendor_key = OPTIONS.avb_keys.get("vendor")
959      vendor_algorithm = OPTIONS.avb_algorithms.get("vendor")
960      with tempfile.NamedTemporaryFile() as image:
961        image.write(data)
962        image.flush()
963        ReplaceKeyInAvbHashtreeFooter(image, vendor_key, vendor_algorithm,
964            misc_info)
965        common.ZipWrite(output_tf_zip, image.name, filename)
966    # A non-APK file; copy it verbatim.
967    else:
968      try:
969        entry = output_tf_zip.getinfo(filename)
970        if output_tf_zip.read(entry) != data:
971          logger.warn(
972              "Output zip contains duplicate entries for %s with different contents", filename)
973        continue
974      except KeyError:
975        common.ZipWriteStr(output_tf_zip, out_info, data)
976
977  if OPTIONS.replace_ota_keys:
978    ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)
979
980
981  # Write back misc_info with the latest values.
982  ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
983
984# Parse string output of `avbtool info_image`.
985def ParseAvbInfo(info_raw):
986  # line_matcher is for parsing each output line of `avbtool info_image`.
987  # example string input: "      Hash Algorithm:        sha1"
988  # example matched input: ("      ", "Hash Algorithm", "sha1")
989  line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$')
990  # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`.
991  # example string input: "example_prop_key -> 'example_prop_value'"
992  # example matched output: ("example_prop_key", "example_prop_value")
993  prop_matcher = re.compile(r"(.+)\s->\s'(.+)'")
994  info = {}
995  indent_stack = [[-1, info]]
996  for line_info_raw in info_raw.split('\n'):
997    # Parse the line
998    line_info_parsed = line_matcher.match(line_info_raw)
999    if not line_info_parsed:
1000      continue
1001    indent = len(line_info_parsed.group(1))
1002    key = line_info_parsed.group(2).strip()
1003    value = line_info_parsed.group(3).strip()
1004
1005    # Pop indentation stack
1006    while indent <= indent_stack[-1][0]:
1007      del indent_stack[-1]
1008
1009    # Insert information into 'info'.
1010    cur_info = indent_stack[-1][1]
1011    if value == "":
1012      if key == "Descriptors":
1013        empty_list = []
1014        cur_info[key] = empty_list
1015        indent_stack.append([indent, empty_list])
1016      else:
1017        empty_dict = {}
1018        cur_info.append({key:empty_dict})
1019        indent_stack.append([indent, empty_dict])
1020    elif key == "Prop":
1021      prop_parsed = prop_matcher.match(value)
1022      if not prop_parsed:
1023        raise ValueError(
1024            "Failed to parse prop while getting avb information.")
1025      cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}})
1026    else:
1027      cur_info[key] = value
1028  return info
1029
1030def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info):
1031  # Get avb information about the image by parsing avbtool info_image.
1032  def GetAvbInfo(avbtool, image_name):
1033    # Get information with raw string by `avbtool info_image`.
1034    info_raw = common.RunAndCheckOutput([
1035      avbtool, 'info_image',
1036      '--image', image_name
1037    ])
1038    return ParseAvbInfo(info_raw)
1039
1040  # Get hashtree descriptor from info
1041  def GetAvbHashtreeDescriptor(avb_info):
1042    hashtree_descriptors = tuple(filter(lambda x: "Hashtree descriptor" in x,
1043        info.get('Descriptors')))
1044    if len(hashtree_descriptors) != 1:
1045      raise ValueError("The number of hashtree descriptor is not 1.")
1046    return hashtree_descriptors[0]["Hashtree descriptor"]
1047
1048  # Get avb info
1049  avbtool = misc_info['avb_avbtool']
1050  info = GetAvbInfo(avbtool, image.name)
1051  hashtree_descriptor = GetAvbHashtreeDescriptor(info)
1052
1053  # Generate command
1054  cmd = [avbtool, 'add_hashtree_footer',
1055    '--key', new_key,
1056    '--algorithm', new_algorithm,
1057    '--partition_name', hashtree_descriptor.get("Partition Name"),
1058    '--partition_size', info.get("Image size").removesuffix(" bytes"),
1059    '--hash_algorithm', hashtree_descriptor.get("Hash Algorithm"),
1060    '--salt', hashtree_descriptor.get("Salt"),
1061    '--do_not_generate_fec',
1062    '--image', image.name
1063  ]
1064
1065  # Append properties into command
1066  props = map(lambda x: x.get("Prop"), filter(lambda x: "Prop" in x,
1067      info.get('Descriptors')))
1068  for prop_wrapped in props:
1069    prop = tuple(prop_wrapped.items())
1070    if len(prop) != 1:
1071      raise ValueError("The number of property is not 1.")
1072    cmd.append('--prop')
1073    cmd.append(prop[0][0] + ':' + prop[0][1])
1074
1075  # Replace Hashtree Footer with new key
1076  common.RunAndCheckOutput(cmd)
1077
1078  # Check root digest is not changed
1079  new_info = GetAvbInfo(avbtool, image.name)
1080  new_hashtree_descriptor = GetAvbHashtreeDescriptor(info)
1081  root_digest = hashtree_descriptor.get("Root Digest")
1082  new_root_digest = new_hashtree_descriptor.get("Root Digest")
1083  assert root_digest == new_root_digest, \
1084      ("Root digest in hashtree descriptor shouldn't be changed. Old: {}, New: "
1085       "{}").format(root_digest, new_root_digest)
1086
1087def ReplaceCerts(data):
1088  """Replaces all the occurences of X.509 certs with the new ones.
1089
1090  The mapping info is read from OPTIONS.key_map. Non-existent certificate will
1091  be skipped. After the replacement, it additionally checks for duplicate
1092  entries, which would otherwise fail the policy loading code in
1093  frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java.
1094
1095  Args:
1096    data: Input string that contains a set of X.509 certs.
1097
1098  Returns:
1099    A string after the replacement.
1100
1101  Raises:
1102    AssertionError: On finding duplicate entries.
1103  """
1104  for old, new in OPTIONS.key_map.items():
1105    if OPTIONS.verbose:
1106      print("    Replacing %s.x509.pem with %s.x509.pem" % (old, new))
1107
1108    try:
1109      with open(old + ".x509.pem") as old_fp:
1110        old_cert16 = base64.b16encode(
1111            common.ParseCertificate(old_fp.read())).decode().lower()
1112      with open(new + ".x509.pem") as new_fp:
1113        new_cert16 = base64.b16encode(
1114            common.ParseCertificate(new_fp.read())).decode().lower()
1115    except IOError as e:
1116      if OPTIONS.verbose or e.errno != errno.ENOENT:
1117        print("    Error accessing %s: %s.\nSkip replacing %s.x509.pem with "
1118              "%s.x509.pem." % (e.filename, e.strerror, old, new))
1119      continue
1120
1121    # Only match entire certs.
1122    pattern = "\\b" + old_cert16 + "\\b"
1123    (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE)
1124
1125    if OPTIONS.verbose:
1126      print("    Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (
1127          num, old, new))
1128
1129  # Verify that there're no duplicate entries after the replacement. Note that
1130  # it's only checking entries with global seinfo at the moment (i.e. ignoring
1131  # the ones with inner packages). (Bug: 69479366)
1132  root = ElementTree.fromstring(data)
1133  signatures = [signer.attrib['signature']
1134                for signer in root.findall('signer')]
1135  assert len(signatures) == len(set(signatures)), \
1136      "Found duplicate entries after cert replacement: {}".format(data)
1137
1138  return data
1139
1140
1141def EditTags(tags):
1142  """Applies the edits to the tag string as specified in OPTIONS.tag_changes.
1143
1144  Args:
1145    tags: The input string that contains comma-separated tags.
1146
1147  Returns:
1148    The updated tags (comma-separated and sorted).
1149  """
1150  tags = set(tags.split(","))
1151  for ch in OPTIONS.tag_changes:
1152    if ch[0] == "-":
1153      tags.discard(ch[1:])
1154    elif ch[0] == "+":
1155      tags.add(ch[1:])
1156  return ",".join(sorted(tags))
1157
1158
1159def RewriteProps(data):
1160  """Rewrites the system properties in the given string.
1161
1162  Each property is expected in 'key=value' format. The properties that contain
1163  build tags (i.e. test-keys, dev-keys) will be updated accordingly by calling
1164  EditTags().
1165
1166  Args:
1167    data: Input string, separated by newlines.
1168
1169  Returns:
1170    The string with modified properties.
1171  """
1172  output = []
1173  for line in data.split("\n"):
1174    line = line.strip()
1175    original_line = line
1176    if line and line[0] != '#' and "=" in line:
1177      key, value = line.split("=", 1)
1178      if (key.startswith("ro.") and
1179              key.endswith((".build.fingerprint", ".build.thumbprint"))):
1180        pieces = value.split("/")
1181        pieces[-1] = EditTags(pieces[-1])
1182        value = "/".join(pieces)
1183      elif key == "ro.bootimage.build.fingerprint":
1184        pieces = value.split("/")
1185        pieces[-1] = EditTags(pieces[-1])
1186        value = "/".join(pieces)
1187      elif key == "ro.build.description":
1188        pieces = value.split()
1189        assert pieces[-1].endswith("-keys")
1190        pieces[-1] = EditTags(pieces[-1])
1191        value = " ".join(pieces)
1192      elif key.startswith("ro.") and key.endswith(".build.tags"):
1193        value = EditTags(value)
1194      elif key == "ro.build.display.id":
1195        # change, eg, "JWR66N dev-keys" to "JWR66N"
1196        value = value.split()
1197        if len(value) > 1 and value[-1].endswith("-keys"):
1198          value.pop()
1199        value = " ".join(value)
1200      line = key + "=" + value
1201    if line != original_line:
1202      print("  replace: ", original_line)
1203      print("     with: ", line)
1204    output.append(line)
1205  return "\n".join(output) + "\n"
1206
1207
1208def WriteOtacerts(output_zip, filename, keys):
1209  """Constructs a zipfile from given keys; and writes it to output_zip.
1210
1211  Args:
1212    output_zip: The output target_files zip.
1213    filename: The archive name in the output zip.
1214    keys: A list of public keys to use during OTA package verification.
1215  """
1216  temp_file = io.BytesIO()
1217  certs_zip = zipfile.ZipFile(temp_file, "w", allowZip64=True)
1218  for k in keys:
1219    common.ZipWrite(certs_zip, k)
1220  common.ZipClose(certs_zip)
1221  common.ZipWriteStr(output_zip, filename, temp_file.getvalue())
1222
1223
1224def ReplaceOtaKeys(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info):
1225  try:
1226    keylist = input_tf_zip.read("META/otakeys.txt").decode().split()
1227  except KeyError:
1228    raise common.ExternalError("can't read META/otakeys.txt from input")
1229
1230  extra_ota_keys_info = misc_info.get("extra_ota_keys")
1231  if extra_ota_keys_info:
1232    extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
1233                      for k in extra_ota_keys_info.split()]
1234    print("extra ota key(s): " + ", ".join(extra_ota_keys))
1235  else:
1236    extra_ota_keys = []
1237  for k in extra_ota_keys:
1238    if not os.path.isfile(k):
1239      raise common.ExternalError(k + " does not exist or is not a file")
1240
1241  extra_recovery_keys_info = misc_info.get("extra_recovery_keys")
1242  if extra_recovery_keys_info:
1243    extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
1244                           for k in extra_recovery_keys_info.split()]
1245    print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
1246  else:
1247    extra_recovery_keys = []
1248  for k in extra_recovery_keys:
1249    if not os.path.isfile(k):
1250      raise common.ExternalError(k + " does not exist or is not a file")
1251
1252  mapped_keys = []
1253  for k in keylist:
1254    m = re.match(r"^(.*)\.x509\.pem$", k)
1255    if not m:
1256      raise common.ExternalError(
1257          "can't parse \"%s\" from META/otakeys.txt" % (k,))
1258    k = m.group(1)
1259    mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")
1260
1261  if mapped_keys:
1262    print("using:\n   ", "\n   ".join(mapped_keys))
1263    print("for OTA package verification")
1264  else:
1265    devkey = misc_info.get("default_system_dev_certificate",
1266                           "build/make/target/product/security/testkey")
1267    mapped_devkey = OPTIONS.key_map.get(devkey, devkey)
1268    if mapped_devkey != devkey:
1269      misc_info["default_system_dev_certificate"] = mapped_devkey
1270    mapped_keys.append(mapped_devkey + ".x509.pem")
1271    print("META/otakeys.txt has no keys; using %s for OTA package"
1272          " verification." % (mapped_keys[0],))
1273  for k in mapped_keys:
1274    if not os.path.isfile(k):
1275      raise common.ExternalError(k + " does not exist or is not a file")
1276
1277  otacerts = [info
1278              for info in input_tf_zip.infolist()
1279              if info.filename.endswith("/otacerts.zip")]
1280  for info in otacerts:
1281    if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")):
1282      extra_keys = extra_recovery_keys
1283    else:
1284      extra_keys = extra_ota_keys
1285    print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys)
1286    WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys)
1287
1288
1289def ReplaceMiscInfoTxt(input_zip, output_zip, misc_info):
1290  """Replaces META/misc_info.txt.
1291
1292  Only writes back the ones in the original META/misc_info.txt. Because the
1293  current in-memory dict contains additional items computed at runtime.
1294  """
1295  misc_info_old = common.LoadDictionaryFromLines(
1296      input_zip.read('META/misc_info.txt').decode().split('\n'))
1297  items = []
1298  for key in sorted(misc_info):
1299    if key in misc_info_old:
1300      items.append('%s=%s' % (key, misc_info[key]))
1301  common.ZipWriteStr(output_zip, "META/misc_info.txt", '\n'.join(items))
1302
1303
1304def ReplaceAvbSigningKeys(misc_info):
1305  """Replaces the AVB signing keys."""
1306
1307  def ReplaceAvbPartitionSigningKey(partition):
1308    key = OPTIONS.avb_keys.get(partition)
1309    if not key:
1310      return
1311
1312    algorithm = OPTIONS.avb_algorithms.get(partition)
1313    assert algorithm, 'Missing AVB signing algorithm for %s' % (partition,)
1314
1315    print('Replacing AVB signing key for %s with "%s" (%s)' % (
1316        partition, key, algorithm))
1317    misc_info['avb_' + partition + '_algorithm'] = algorithm
1318    misc_info['avb_' + partition + '_key_path'] = key
1319
1320    extra_args = OPTIONS.avb_extra_args.get(partition)
1321    if extra_args:
1322      print('Setting extra AVB signing args for %s to "%s"' % (
1323          partition, extra_args))
1324      args_key = AVB_FOOTER_ARGS_BY_PARTITION.get(
1325          partition,
1326          # custom partition
1327          "avb_{}_add_hashtree_footer_args".format(partition))
1328      misc_info[args_key] = (misc_info.get(args_key, '') + ' ' + extra_args)
1329
1330  for partition in AVB_FOOTER_ARGS_BY_PARTITION:
1331    ReplaceAvbPartitionSigningKey(partition)
1332
1333  for custom_partition in misc_info.get(
1334          "avb_custom_images_partition_list", "").strip().split():
1335    ReplaceAvbPartitionSigningKey(custom_partition)
1336
1337
1338def RewriteAvbProps(misc_info):
1339  """Rewrites the props in AVB signing args."""
1340  for partition, args_key in AVB_FOOTER_ARGS_BY_PARTITION.items():
1341    args = misc_info.get(args_key)
1342    if not args:
1343      continue
1344
1345    tokens = []
1346    changed = False
1347    for token in args.split():
1348      fingerprint_key = 'com.android.build.{}.fingerprint'.format(partition)
1349      if not token.startswith(fingerprint_key):
1350        tokens.append(token)
1351        continue
1352      prefix, tag = token.rsplit('/', 1)
1353      tokens.append('{}/{}'.format(prefix, EditTags(tag)))
1354      changed = True
1355
1356    if changed:
1357      result = ' '.join(tokens)
1358      print('Rewriting AVB prop for {}:\n'.format(partition))
1359      print('  replace: {}'.format(args))
1360      print('     with: {}'.format(result))
1361      misc_info[args_key] = result
1362
1363
1364def BuildKeyMap(misc_info, key_mapping_options):
1365  for s, d in key_mapping_options:
1366    if s is None:   # -d option
1367      devkey = misc_info.get("default_system_dev_certificate",
1368                             "build/make/target/product/security/testkey")
1369      devkeydir = os.path.dirname(devkey)
1370
1371      OPTIONS.key_map.update({
1372          devkeydir + "/testkey":  d + "/releasekey",
1373          devkeydir + "/devkey":   d + "/releasekey",
1374          devkeydir + "/media":    d + "/media",
1375          devkeydir + "/shared":   d + "/shared",
1376          devkeydir + "/platform": d + "/platform",
1377          devkeydir + "/networkstack": d + "/networkstack",
1378          devkeydir + "/sdk_sandbox": d + "/sdk_sandbox",
1379      })
1380    else:
1381      OPTIONS.key_map[s] = d
1382
1383
1384def GetApiLevelAndCodename(input_tf_zip):
1385  data = input_tf_zip.read("SYSTEM/build.prop").decode()
1386  api_level = None
1387  codename = None
1388  for line in data.split("\n"):
1389    line = line.strip()
1390    if line and line[0] != '#' and "=" in line:
1391      key, value = line.split("=", 1)
1392      key = key.strip()
1393      if key == "ro.build.version.sdk":
1394        api_level = int(value.strip())
1395      elif key == "ro.build.version.codename":
1396        codename = value.strip()
1397
1398  if api_level is None:
1399    raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1400  if codename is None:
1401    raise ValueError("No ro.build.version.codename in SYSTEM/build.prop")
1402
1403  return (api_level, codename)
1404
1405
1406def GetCodenameToApiLevelMap(input_tf_zip):
1407  data = input_tf_zip.read("SYSTEM/build.prop").decode()
1408  api_level = None
1409  codenames = None
1410  for line in data.split("\n"):
1411    line = line.strip()
1412    if line and line[0] != '#' and "=" in line:
1413      key, value = line.split("=", 1)
1414      key = key.strip()
1415      if key == "ro.build.version.sdk":
1416        api_level = int(value.strip())
1417      elif key == "ro.build.version.all_codenames":
1418        codenames = value.strip().split(",")
1419
1420  if api_level is None:
1421    raise ValueError("No ro.build.version.sdk in SYSTEM/build.prop")
1422  if codenames is None:
1423    raise ValueError("No ro.build.version.all_codenames in SYSTEM/build.prop")
1424
1425  result = {}
1426  for codename in codenames:
1427    codename = codename.strip()
1428    if codename:
1429      result[codename] = api_level
1430  return result
1431
1432
1433def ReadApexKeysInfo(tf_zip):
1434  """Parses the APEX keys info from a given target-files zip.
1435
1436  Given a target-files ZipFile, parses the META/apexkeys.txt entry and returns a
1437  dict that contains the mapping from APEX names (e.g. com.android.tzdata) to a
1438  tuple of (payload_key, container_key, sign_tool).
1439
1440  Args:
1441    tf_zip: The input target_files ZipFile (already open).
1442
1443  Returns:
1444    (payload_key, container_key, sign_tool):
1445      - payload_key contains the path to the payload signing key
1446      - container_key contains the path to the container signing key
1447      - sign_tool is an apex-specific signing tool for its payload contents
1448  """
1449  keys = {}
1450  for line in tf_zip.read('META/apexkeys.txt').decode().split('\n'):
1451    line = line.strip()
1452    if not line:
1453      continue
1454    matches = re.match(
1455        r'^name="(?P<NAME>.*)"\s+'
1456        r'public_key="(?P<PAYLOAD_PUBLIC_KEY>.*)"\s+'
1457        r'private_key="(?P<PAYLOAD_PRIVATE_KEY>.*)"\s+'
1458        r'container_certificate="(?P<CONTAINER_CERT>.*)"\s+'
1459        r'container_private_key="(?P<CONTAINER_PRIVATE_KEY>.*?)"'
1460        r'(\s+partition="(?P<PARTITION>.*?)")?'
1461        r'(\s+sign_tool="(?P<SIGN_TOOL>.*?)")?$',
1462        line)
1463    if not matches:
1464      continue
1465
1466    name = matches.group('NAME')
1467    payload_private_key = matches.group("PAYLOAD_PRIVATE_KEY")
1468
1469    def CompareKeys(pubkey, pubkey_suffix, privkey, privkey_suffix):
1470      pubkey_suffix_len = len(pubkey_suffix)
1471      privkey_suffix_len = len(privkey_suffix)
1472      return (pubkey.endswith(pubkey_suffix) and
1473              privkey.endswith(privkey_suffix) and
1474              pubkey[:-pubkey_suffix_len] == privkey[:-privkey_suffix_len])
1475
1476    # Check the container key names, as we'll carry them without the
1477    # extensions. This doesn't apply to payload keys though, which we will use
1478    # full names only.
1479    container_cert = matches.group("CONTAINER_CERT")
1480    container_private_key = matches.group("CONTAINER_PRIVATE_KEY")
1481    if container_cert == 'PRESIGNED' and container_private_key == 'PRESIGNED':
1482      container_key = 'PRESIGNED'
1483    elif CompareKeys(
1484            container_cert, OPTIONS.public_key_suffix,
1485            container_private_key, OPTIONS.private_key_suffix):
1486      container_key = container_cert[:-len(OPTIONS.public_key_suffix)]
1487    else:
1488      raise ValueError("Failed to parse container keys: \n{}".format(line))
1489
1490    sign_tool = matches.group("SIGN_TOOL")
1491    keys[name] = (payload_private_key, container_key, sign_tool)
1492
1493  return keys
1494
1495
1496def BuildVendorPartitions(output_zip_path):
1497  """Builds OPTIONS.vendor_partitions using OPTIONS.vendor_otatools."""
1498  if OPTIONS.vendor_partitions.difference(ALLOWED_VENDOR_PARTITIONS):
1499    logger.warning("Allowed --vendor_partitions: %s",
1500                   ",".join(ALLOWED_VENDOR_PARTITIONS))
1501    OPTIONS.vendor_partitions = ALLOWED_VENDOR_PARTITIONS.intersection(
1502        OPTIONS.vendor_partitions)
1503
1504  logger.info("Building vendor partitions using vendor otatools.")
1505  vendor_tempdir = common.UnzipTemp(output_zip_path, [
1506      "META/*",
1507      "SYSTEM/build.prop",
1508      "RECOVERY/*",
1509      "BOOT/*",
1510      "OTA/",
1511  ] + ["{}/*".format(p.upper()) for p in OPTIONS.vendor_partitions])
1512
1513  # Disable various partitions that build based on misc_info fields.
1514  # Only partitions in ALLOWED_VENDOR_PARTITIONS can be rebuilt using
1515  # vendor otatools. These other partitions will be rebuilt using the main
1516  # otatools if necessary.
1517  vendor_misc_info_path = os.path.join(vendor_tempdir, "META/misc_info.txt")
1518  vendor_misc_info = common.LoadDictionaryFromFile(vendor_misc_info_path)
1519  # Ignore if not rebuilding recovery
1520  if not OPTIONS.rebuild_recovery:
1521    vendor_misc_info["no_boot"] = "true"  # boot
1522    vendor_misc_info["vendor_boot"] = "false"  # vendor_boot
1523    vendor_misc_info["no_recovery"] = "true"  # recovery
1524    vendor_misc_info["avb_enable"] = "false"  # vbmeta
1525
1526  vendor_misc_info["has_dtbo"] = "false"  # dtbo
1527  vendor_misc_info["has_pvmfw"] = "false"  # pvmfw
1528  vendor_misc_info["avb_custom_images_partition_list"] = ""  # avb custom images
1529  vendor_misc_info["avb_building_vbmeta_image"] = "false" # skip building vbmeta
1530  vendor_misc_info["custom_images_partition_list"] = ""  # custom images
1531  vendor_misc_info["use_dynamic_partitions"] = "false"  # super_empty
1532  vendor_misc_info["build_super_partition"] = "false"  # super split
1533  vendor_misc_info["avb_vbmeta_system"] = ""  # skip building vbmeta_system
1534  with open(vendor_misc_info_path, "w") as output:
1535    for key in sorted(vendor_misc_info):
1536      output.write("{}={}\n".format(key, vendor_misc_info[key]))
1537
1538  # Disable system partition by a placeholder of IMAGES/system.img,
1539  # instead of removing SYSTEM folder.
1540  # Because SYSTEM/build.prop is still needed for:
1541  #   add_img_to_target_files.CreateImage ->
1542  #   common.BuildInfo ->
1543  #   common.BuildInfo.CalculateFingerprint
1544  vendor_images_path = os.path.join(vendor_tempdir, "IMAGES")
1545  if not os.path.exists(vendor_images_path):
1546    os.makedirs(vendor_images_path)
1547  with open(os.path.join(vendor_images_path, "system.img"), "w") as output:
1548    pass
1549
1550  # Disable care_map.pb as not all ab_partitions are available when
1551  # vendor otatools regenerates vendor images.
1552  if os.path.exists(os.path.join(vendor_tempdir, "META/ab_partitions.txt")):
1553    os.remove(os.path.join(vendor_tempdir, "META/ab_partitions.txt"))
1554  # Disable RADIO images
1555  if os.path.exists(os.path.join(vendor_tempdir, "META/pack_radioimages.txt")):
1556    os.remove(os.path.join(vendor_tempdir, "META/pack_radioimages.txt"))
1557
1558  # Build vendor images using vendor otatools.
1559  # Accept either a zip file or extracted directory.
1560  if os.path.isfile(OPTIONS.vendor_otatools):
1561    vendor_otatools_dir = common.MakeTempDir(prefix="vendor_otatools_")
1562    common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1563  else:
1564    vendor_otatools_dir = OPTIONS.vendor_otatools
1565  cmd = [
1566      os.path.join(vendor_otatools_dir, "bin", "add_img_to_target_files"),
1567      "--is_signing",
1568      "--add_missing",
1569      "--verbose",
1570      vendor_tempdir,
1571  ]
1572  if OPTIONS.rebuild_recovery:
1573    cmd.insert(4, "--rebuild_recovery")
1574
1575  common.RunAndCheckOutput(cmd, verbose=True)
1576
1577  logger.info("Writing vendor partitions to output archive.")
1578  with zipfile.ZipFile(
1579      output_zip_path, "a", compression=zipfile.ZIP_DEFLATED,
1580      allowZip64=True) as output_zip:
1581    for p in OPTIONS.vendor_partitions:
1582      img_file_path = "IMAGES/{}.img".format(p)
1583      map_file_path = "IMAGES/{}.map".format(p)
1584      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, img_file_path), img_file_path)
1585      if os.path.exists(os.path.join(vendor_tempdir, map_file_path)):
1586        common.ZipWrite(output_zip, os.path.join(vendor_tempdir, map_file_path), map_file_path)
1587    # copy recovery.img, boot.img, recovery patch & install.sh
1588    if OPTIONS.rebuild_recovery:
1589      recovery_img = "IMAGES/recovery.img"
1590      boot_img = "IMAGES/boot.img"
1591      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_img), recovery_img)
1592      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, boot_img), boot_img)
1593      recovery_patch_path = "VENDOR/recovery-from-boot.p"
1594      recovery_sh_path = "VENDOR/bin/install-recovery.sh"
1595      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_patch_path), recovery_patch_path)
1596      common.ZipWrite(output_zip, os.path.join(vendor_tempdir, recovery_sh_path), recovery_sh_path)
1597
1598
1599def main(argv):
1600
1601  key_mapping_options = []
1602
1603  def option_handler(o, a):
1604    if o in ("-e", "--extra_apks"):
1605      names, key = a.split("=")
1606      names = names.split(",")
1607      for n in names:
1608        OPTIONS.extra_apks[n] = key
1609    elif o == "--extra_apex_payload_key":
1610      apex_names, key = a.split("=")
1611      for name in apex_names.split(","):
1612        OPTIONS.extra_apex_payload_keys[name] = key
1613    elif o == "--skip_apks_with_path_prefix":
1614      # Check the prefix, which must be in all upper case.
1615      prefix = a.split('/')[0]
1616      if not prefix or prefix != prefix.upper():
1617        raise ValueError("Invalid path prefix '%s'" % (a,))
1618      OPTIONS.skip_apks_with_path_prefix.add(a)
1619    elif o in ("-d", "--default_key_mappings"):
1620      key_mapping_options.append((None, a))
1621    elif o in ("-k", "--key_mapping"):
1622      key_mapping_options.append(a.split("=", 1))
1623    elif o in ("-o", "--replace_ota_keys"):
1624      OPTIONS.replace_ota_keys = True
1625    elif o in ("-t", "--tag_changes"):
1626      new = []
1627      for i in a.split(","):
1628        i = i.strip()
1629        if not i or i[0] not in "-+":
1630          raise ValueError("Bad tag change '%s'" % (i,))
1631        new.append(i[0] + i[1:].strip())
1632      OPTIONS.tag_changes = tuple(new)
1633    elif o == "--replace_verity_public_key":
1634      raise ValueError("--replace_verity_public_key is no longer supported,"
1635                       " please switch to AVB")
1636    elif o == "--replace_verity_private_key":
1637      raise ValueError("--replace_verity_private_key is no longer supported,"
1638                       " please switch to AVB")
1639    elif o == "--replace_verity_keyid":
1640      raise ValueError("--replace_verity_keyid is no longer supported, please"
1641                       " switch to AVB")
1642    elif o == "--remove_avb_public_keys":
1643      OPTIONS.remove_avb_public_keys = a.split(",")
1644    elif o == "--avb_vbmeta_key":
1645      OPTIONS.avb_keys['vbmeta'] = a
1646    elif o == "--avb_vbmeta_algorithm":
1647      OPTIONS.avb_algorithms['vbmeta'] = a
1648    elif o == "--avb_vbmeta_extra_args":
1649      OPTIONS.avb_extra_args['vbmeta'] = a
1650    elif o == "--avb_boot_key":
1651      OPTIONS.avb_keys['boot'] = a
1652    elif o == "--avb_boot_algorithm":
1653      OPTIONS.avb_algorithms['boot'] = a
1654    elif o == "--avb_boot_extra_args":
1655      OPTIONS.avb_extra_args['boot'] = a
1656    elif o == "--avb_dtbo_key":
1657      OPTIONS.avb_keys['dtbo'] = a
1658    elif o == "--avb_dtbo_algorithm":
1659      OPTIONS.avb_algorithms['dtbo'] = a
1660    elif o == "--avb_dtbo_extra_args":
1661      OPTIONS.avb_extra_args['dtbo'] = a
1662    elif o == "--avb_init_boot_key":
1663      OPTIONS.avb_keys['init_boot'] = a
1664    elif o == "--avb_init_boot_algorithm":
1665      OPTIONS.avb_algorithms['init_boot'] = a
1666    elif o == "--avb_init_boot_extra_args":
1667      OPTIONS.avb_extra_args['init_boot'] = a
1668    elif o == "--avb_recovery_key":
1669      OPTIONS.avb_keys['recovery'] = a
1670    elif o == "--avb_recovery_algorithm":
1671      OPTIONS.avb_algorithms['recovery'] = a
1672    elif o == "--avb_recovery_extra_args":
1673      OPTIONS.avb_extra_args['recovery'] = a
1674    elif o == "--avb_system_key":
1675      OPTIONS.avb_keys['system'] = a
1676    elif o == "--avb_system_algorithm":
1677      OPTIONS.avb_algorithms['system'] = a
1678    elif o == "--avb_system_extra_args":
1679      OPTIONS.avb_extra_args['system'] = a
1680    elif o == "--avb_system_other_key":
1681      OPTIONS.avb_keys['system_other'] = a
1682    elif o == "--avb_system_other_algorithm":
1683      OPTIONS.avb_algorithms['system_other'] = a
1684    elif o == "--avb_system_other_extra_args":
1685      OPTIONS.avb_extra_args['system_other'] = a
1686    elif o == "--avb_vendor_key":
1687      OPTIONS.avb_keys['vendor'] = a
1688    elif o == "--avb_vendor_algorithm":
1689      OPTIONS.avb_algorithms['vendor'] = a
1690    elif o == "--avb_vendor_extra_args":
1691      OPTIONS.avb_extra_args['vendor'] = a
1692    elif o == "--avb_vbmeta_system_key":
1693      OPTIONS.avb_keys['vbmeta_system'] = a
1694    elif o == "--avb_vbmeta_system_algorithm":
1695      OPTIONS.avb_algorithms['vbmeta_system'] = a
1696    elif o == "--avb_vbmeta_system_extra_args":
1697      OPTIONS.avb_extra_args['vbmeta_system'] = a
1698    elif o == "--avb_vbmeta_vendor_key":
1699      OPTIONS.avb_keys['vbmeta_vendor'] = a
1700    elif o == "--avb_vbmeta_vendor_algorithm":
1701      OPTIONS.avb_algorithms['vbmeta_vendor'] = a
1702    elif o == "--avb_vbmeta_vendor_extra_args":
1703      OPTIONS.avb_extra_args['vbmeta_vendor'] = a
1704    elif o == "--avb_apex_extra_args":
1705      OPTIONS.avb_extra_args['apex'] = a
1706    elif o == "--avb_extra_custom_image_key":
1707      partition, key = a.split("=")
1708      OPTIONS.avb_keys[partition] = key
1709    elif o == "--avb_extra_custom_image_algorithm":
1710      partition, algorithm = a.split("=")
1711      OPTIONS.avb_algorithms[partition] = algorithm
1712    elif o == "--avb_extra_custom_image_extra_args":
1713      # Setting the maxsplit parameter to one, which will return a list with
1714      # two elements. e.g., the second '=' should not be splitted for
1715      # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
1716      partition, extra_args = a.split("=", 1)
1717      OPTIONS.avb_extra_args[partition] = extra_args
1718    elif o == "--vendor_otatools":
1719      OPTIONS.vendor_otatools = a
1720    elif o == "--vendor_partitions":
1721      OPTIONS.vendor_partitions = set(a.split(","))
1722    elif o == "--allow_gsi_debug_sepolicy":
1723      OPTIONS.allow_gsi_debug_sepolicy = True
1724    elif o == "--override_apk_keys":
1725      OPTIONS.override_apk_keys = a
1726    elif o == "--override_apex_keys":
1727      OPTIONS.override_apex_keys = a
1728    elif o in ("--gki_signing_key",  "--gki_signing_algorithm",  "--gki_signing_extra_args"):
1729      print(f"{o} is deprecated and does nothing")
1730    else:
1731      return False
1732    return True
1733
1734  args = common.ParseOptions(
1735      argv, __doc__,
1736      extra_opts="e:d:k:ot:",
1737      extra_long_opts=[
1738          "extra_apks=",
1739          "extra_apex_payload_key=",
1740          "skip_apks_with_path_prefix=",
1741          "default_key_mappings=",
1742          "key_mapping=",
1743          "replace_ota_keys",
1744          "tag_changes=",
1745          "replace_verity_public_key=",
1746          "replace_verity_private_key=",
1747          "replace_verity_keyid=",
1748          "remove_avb_public_keys=",
1749          "avb_apex_extra_args=",
1750          "avb_vbmeta_algorithm=",
1751          "avb_vbmeta_key=",
1752          "avb_vbmeta_extra_args=",
1753          "avb_boot_algorithm=",
1754          "avb_boot_key=",
1755          "avb_boot_extra_args=",
1756          "avb_dtbo_algorithm=",
1757          "avb_dtbo_key=",
1758          "avb_dtbo_extra_args=",
1759          "avb_init_boot_algorithm=",
1760          "avb_init_boot_key=",
1761          "avb_init_boot_extra_args=",
1762          "avb_recovery_algorithm=",
1763          "avb_recovery_key=",
1764          "avb_recovery_extra_args=",
1765          "avb_system_algorithm=",
1766          "avb_system_key=",
1767          "avb_system_extra_args=",
1768          "avb_system_other_algorithm=",
1769          "avb_system_other_key=",
1770          "avb_system_other_extra_args=",
1771          "avb_vendor_algorithm=",
1772          "avb_vendor_key=",
1773          "avb_vendor_extra_args=",
1774          "avb_vbmeta_system_algorithm=",
1775          "avb_vbmeta_system_key=",
1776          "avb_vbmeta_system_extra_args=",
1777          "avb_vbmeta_vendor_algorithm=",
1778          "avb_vbmeta_vendor_key=",
1779          "avb_vbmeta_vendor_extra_args=",
1780          "avb_extra_custom_image_key=",
1781          "avb_extra_custom_image_algorithm=",
1782          "avb_extra_custom_image_extra_args=",
1783          "gki_signing_key=",
1784          "gki_signing_algorithm=",
1785          "gki_signing_extra_args=",
1786          "vendor_partitions=",
1787          "vendor_otatools=",
1788          "allow_gsi_debug_sepolicy",
1789          "override_apk_keys=",
1790          "override_apex_keys=",
1791      ],
1792      extra_option_handler=[option_handler, payload_signer.signer_options])
1793
1794  if len(args) != 2:
1795    common.Usage(__doc__)
1796    sys.exit(1)
1797
1798  common.InitLogging()
1799
1800  input_zip = zipfile.ZipFile(args[0], "r", allowZip64=True)
1801  output_zip = zipfile.ZipFile(args[1], "w",
1802                               compression=zipfile.ZIP_DEFLATED,
1803                               allowZip64=True)
1804
1805  misc_info = common.LoadInfoDict(input_zip)
1806  if OPTIONS.package_key is None:
1807      OPTIONS.package_key = misc_info.get(
1808          "default_system_dev_certificate",
1809          "build/make/target/product/security/testkey")
1810
1811  BuildKeyMap(misc_info, key_mapping_options)
1812
1813  apk_keys_info, compressed_extension = common.ReadApkCerts(input_zip)
1814  apk_keys = GetApkCerts(apk_keys_info)
1815
1816  apex_keys_info = ReadApexKeysInfo(input_zip)
1817  apex_keys = GetApexKeys(apex_keys_info, apk_keys)
1818
1819  # TODO(xunchang) check for the apks inside the apex files, and abort early if
1820  # the keys are not available.
1821  CheckApkAndApexKeysAvailable(
1822      input_zip,
1823      set(apk_keys.keys()) | set(apex_keys.keys()),
1824      compressed_extension,
1825      apex_keys)
1826
1827  key_passwords = common.GetKeyPasswords(
1828      set(apk_keys.values()) | set(itertools.chain(*apex_keys.values())))
1829  platform_api_level, _ = GetApiLevelAndCodename(input_zip)
1830  codename_to_api_level_map = GetCodenameToApiLevelMap(input_zip)
1831
1832  ProcessTargetFiles(input_zip, output_zip, misc_info,
1833                     apk_keys, apex_keys, key_passwords,
1834                     platform_api_level, codename_to_api_level_map,
1835                     compressed_extension)
1836
1837  common.ZipClose(input_zip)
1838  common.ZipClose(output_zip)
1839
1840  if OPTIONS.vendor_partitions and OPTIONS.vendor_otatools:
1841    BuildVendorPartitions(args[1])
1842
1843  # Skip building userdata.img and cache.img when signing the target files.
1844  new_args = ["--is_signing", "--add_missing", "--verbose"]
1845  # add_img_to_target_files builds the system image from scratch, so the
1846  # recovery patch is guaranteed to be regenerated there.
1847  if OPTIONS.rebuild_recovery:
1848    new_args.append("--rebuild_recovery")
1849  new_args.append(args[1])
1850  add_img_to_target_files.main(new_args)
1851
1852  print("done.")
1853
1854
1855if __name__ == '__main__':
1856  try:
1857    main(sys.argv[1:])
1858  finally:
1859    common.Cleanup()
1860