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