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