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