Esempio n. 1
0
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, apk_keys,
                       apex_keys, key_passwords, platform_api_level,
                       codename_to_api_level_map, compressed_extension):
    # maxsize measures the maximum filename length, including the ones to be
    # skipped.
    maxsize = max([
        len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
        if GetApkFileInfo(i.filename, compressed_extension, [])[0]
    ])
    system_root_image = misc_info.get("system_root_image") == "true"

    for info in input_tf_zip.infolist():
        filename = info.filename
        if filename.startswith("IMAGES/"):
            continue

        # Skip split super images, which will be re-generated during signing.
        if filename.startswith("OTA/") and filename.endswith(".img"):
            continue

        data = input_tf_zip.read(filename)
        out_info = copy.copy(info)
        (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
            filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)

        if is_apk and should_be_skipped:
            # Copy skipped APKs verbatim.
            print("NOT signing: %s\n"
                  "        (skipped due to matching prefix)" % (filename, ))
            common.ZipWriteStr(output_tf_zip, out_info, data)

        # Sign APKs.
        elif is_apk:
            name = os.path.basename(filename)
            if is_compressed:
                name = name[:-len(compressed_extension)]

            key = apk_keys[name]
            if key not in common.SPECIAL_CERT_STRINGS:
                print("    signing: %-*s (%s)" % (maxsize, name, key))
                signed_data = SignApk(data, key, key_passwords[key],
                                      platform_api_level,
                                      codename_to_api_level_map, is_compressed)
                common.ZipWriteStr(output_tf_zip, out_info, signed_data)
            else:
                # an APK we're not supposed to sign.
                print("NOT signing: %s\n"
                      "        (skipped due to special cert string)" %
                      (name, ))
                common.ZipWriteStr(output_tf_zip, out_info, data)

        # Sign bundled APEX files.
        elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
            name = os.path.basename(filename)
            payload_key, container_key = apex_keys[name]

            # We've asserted not having a case with only one of them PRESIGNED.
            if (payload_key not in common.SPECIAL_CERT_STRINGS
                    and container_key not in common.SPECIAL_CERT_STRINGS):
                print("    signing: %-*s container (%s)" %
                      (maxsize, name, container_key))
                print("           : %-*s payload   (%s)" %
                      (maxsize, name, payload_key))

                signed_apex = apex_utils.SignApex(
                    data, payload_key, container_key,
                    key_passwords[container_key], codename_to_api_level_map,
                    OPTIONS.avb_extra_args.get('apex'))
                common.ZipWrite(output_tf_zip, signed_apex, filename)

            else:
                print("NOT signing: %s\n"
                      "        (skipped due to special cert string)" %
                      (name, ))
                common.ZipWriteStr(output_tf_zip, out_info, data)

        # AVB public keys for the installed APEXes, which will be updated later.
        elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex'
              and filename != 'SYSTEM/etc/security/apex/'):
            continue

        # System properties.
        elif filename in (
                "SYSTEM/build.prop",
                "VENDOR/build.prop",
                "SYSTEM/vendor/build.prop",
                "ODM/etc/build.prop",
                "VENDOR/odm/etc/build.prop",
                "PRODUCT/build.prop",
                "SYSTEM/product/build.prop",
                "PRODUCT_SERVICES/build.prop",
                "SYSTEM/product_services/build.prop",
                "SYSTEM/etc/prop.default",
                "BOOT/RAMDISK/prop.default",
                "RECOVERY/RAMDISK/prop.default",

                # ROOT/default.prop is a legacy path, but may still exist for upgrading
                # devices that don't support `property_overrides_split_enabled`.
                "ROOT/default.prop",

                # RECOVERY/RAMDISK/default.prop is a legacy path, but will always exist
                # as a symlink in the current code. So it's a no-op here. Keeping the
                # path here for clarity.
                "RECOVERY/RAMDISK/default.prop"):
            print("Rewriting %s:" % (filename, ))
            if stat.S_ISLNK(info.external_attr >> 16):
                new_data = data
            else:
                new_data = RewriteProps(data)
            common.ZipWriteStr(output_tf_zip, out_info, new_data)

        # Replace the certs in *mac_permissions.xml (there could be multiple, such
        # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
        elif filename.endswith("mac_permissions.xml"):
            print("Rewriting %s with new keys." % (filename, ))
            new_data = ReplaceCerts(data)
            common.ZipWriteStr(output_tf_zip, out_info, new_data)

        # Ask add_img_to_target_files to rebuild the recovery patch if needed.
        elif filename in ("SYSTEM/recovery-from-boot.p",
                          "SYSTEM/etc/recovery.img",
                          "SYSTEM/bin/install-recovery.sh"):
            OPTIONS.rebuild_recovery = True

        # Don't copy OTA certs if we're replacing them.
        elif (
                OPTIONS.replace_ota_keys and filename in
            ("BOOT/RAMDISK/system/etc/security/otacerts.zip",
             "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
             "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
             "SYSTEM/etc/security/otacerts.zip",
             "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
            pass

        # Skip META/misc_info.txt since we will write back the new values later.
        elif filename == "META/misc_info.txt":
            pass

        # Skip verity public key if we will replace it.
        elif (OPTIONS.replace_verity_public_key
              and filename in ("BOOT/RAMDISK/verity_key", "ROOT/verity_key")):
            pass

        # Skip verity keyid (for system_root_image use) if we will replace it.
        elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
            pass

        # Skip the care_map as we will regenerate the system/vendor images.
        elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
            pass

        # Updates system_other.avbpubkey in /product/etc/.
        elif filename in (
                "PRODUCT/etc/security/avb/system_other.avbpubkey",
                "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
            # Only update system_other's public key, if the corresponding signing
            # key is specified via --avb_system_other_key.
            signing_key = OPTIONS.avb_keys.get("system_other")
            if signing_key:
                public_key = common.ExtractAvbPublicKey(signing_key)
                print(
                    "    Rewriting AVB public key of system_other in /product")
                common.ZipWrite(output_tf_zip, public_key, filename)

        # Should NOT sign boot-debug.img.
        elif filename in (
                "BOOT/RAMDISK/force_debuggable",
                "RECOVERY/RAMDISK/force_debuggable"
                "RECOVERY/RAMDISK/first_stage_ramdisk/force_debuggable"):
            raise common.ExternalError("debuggable boot.img cannot be signed")

        # A non-APK file; copy it verbatim.
        else:
            common.ZipWriteStr(output_tf_zip, out_info, data)

    if OPTIONS.replace_ota_keys:
        ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)

    # Replace the keyid string in misc_info dict.
    if OPTIONS.replace_verity_private_key:
        ReplaceVerityPrivateKey(misc_info,
                                OPTIONS.replace_verity_private_key[1])

    if OPTIONS.replace_verity_public_key:
        dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
        # We are replacing the one in boot image only, since the one under
        # recovery won't ever be needed.
        ReplaceVerityPublicKey(output_tf_zip, dest,
                               OPTIONS.replace_verity_public_key[1])

    # Replace the keyid string in BOOT/cmdline.
    if OPTIONS.replace_verity_keyid:
        ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
                           OPTIONS.replace_verity_keyid[1])

    # Replace the AVB signing keys, if any.
    ReplaceAvbSigningKeys(misc_info)

    # Write back misc_info with the latest values.
    ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
def SignApex(apex_data,
             payload_key,
             container_key,
             container_pw,
             codename_to_api_level_map,
             signing_args=None):
    """Signs the current APEX with the given payload/container keys.

  Args:
    apex_data: Raw APEX data.
    payload_key: The path to payload signing key (w/ extension).
    container_key: The path to container signing key (w/o extension).
    container_pw: The matching password of the container_key, or None.
    codename_to_api_level_map: A dict that maps from codename to API level.
    signing_args: Additional args to be passed to the payload signer.

  Returns:
    The path to the signed APEX file.
  """
    apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
    with open(apex_file, 'wb') as apex_fp:
        apex_fp.write(apex_data)

    APEX_PAYLOAD_IMAGE = 'apex_payload.img'
    APEX_PUBKEY = 'apex_pubkey'

    # 1a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
    # payload_key.
    payload_dir = common.MakeTempDir(prefix='apex-payload-')
    with zipfile.ZipFile(apex_file) as apex_fd:
        payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)

    payload_info = apex_utils.ParseApexPayloadInfo(payload_file)
    apex_utils.SignApexPayload(payload_file, payload_key,
                               payload_info['apex.key'],
                               payload_info['Algorithm'], payload_info['Salt'],
                               signing_args)

    # 1b. Update the embedded payload public key.
    payload_public_key = common.ExtractAvbPublicKey(payload_key)

    common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
    common.ZipDelete(apex_file, APEX_PUBKEY)
    apex_zip = zipfile.ZipFile(apex_file, 'a')
    common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
    common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
    common.ZipClose(apex_zip)

    # 2. Align the files at page boundary (same as in apexer).
    aligned_apex = common.MakeTempFile(prefix='apex-container-',
                                       suffix='.apex')
    common.RunAndCheckOutput(
        ['zipalign', '-f', '4096', apex_file, aligned_apex])

    # 3. Sign the APEX container with container_key.
    signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')

    # Specify the 4K alignment when calling SignApk.
    extra_signapk_args = OPTIONS.extra_signapk_args[:]
    extra_signapk_args.extend(['-a', '4096'])

    common.SignFile(aligned_apex,
                    signed_apex,
                    container_key,
                    container_pw,
                    codename_to_api_level_map=codename_to_api_level_map,
                    extra_signapk_args=extra_signapk_args)

    return signed_apex
def AddImagesToTargetFiles(filename):
    OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)

    if not OPTIONS.add_missing:
        for n in input_zip.namelist():
            if n.startswith("IMAGES/"):
                print("target_files appears to already contain images.")
                sys.exit(1)

    try:
        input_zip.getinfo("VENDOR/")
        has_vendor = True
    except KeyError:
        has_vendor = False

    try:
        input_zip.getinfo("OEM/")
        has_oem = True
    except KeyError:
        has_oem = False

    has_system_other = "SYSTEM_OTHER/" in input_zip.namelist()

    OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

    common.ZipClose(input_zip)
    output_zip = zipfile.ZipFile(filename,
                                 "a",
                                 compression=zipfile.ZIP_DEFLATED)

    has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")

    def banner(s):
        print("\n\n++++ " + s + " ++++\n\n")

    banner("boot")
    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
    boot_image = None
    if os.path.exists(prebuilt_path):
        print("boot.img already exists in IMAGES/, no need to rebuild...")
        if OPTIONS.rebuild_recovery:
            boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                                 OPTIONS.input_tmp, "BOOT")
    else:
        boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                             OPTIONS.input_tmp, "BOOT")
        if boot_image:
            boot_image.AddToZip(output_zip)

    recovery_image = None
    if has_recovery:
        banner("recovery")
        prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES",
                                     "recovery.img")
        if os.path.exists(prebuilt_path):
            print(
                "recovery.img already exists in IMAGES/, no need to rebuild..."
            )
            if OPTIONS.rebuild_recovery:
                recovery_image = common.GetBootableImage(
                    "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
                    "RECOVERY")
        else:
            recovery_image = common.GetBootableImage("IMAGES/recovery.img",
                                                     "recovery.img",
                                                     OPTIONS.input_tmp,
                                                     "RECOVERY")
            if recovery_image:
                recovery_image.AddToZip(output_zip)

    banner("system")
    system_imgname = AddSystem(output_zip,
                               recovery_img=recovery_image,
                               boot_img=boot_image)
    vendor_imgname = None
    if has_vendor:
        banner("vendor")
        vendor_imgname = AddVendor(output_zip)
    if has_system_other:
        banner("system_other")
        AddSystemOther(output_zip)
    banner("userdata")
    AddUserdata(output_zip)
    banner("extrauserdata")
    AddUserdataExtra(output_zip)
    banner("cache")
    AddCache(output_zip)
    if has_oem:
        banner("oem")
        AddOem(output_zip)

    # For devices using A/B update, copy over images from RADIO/ to IMAGES/ and
    # make sure we have all the needed images ready under IMAGES/.
    ab_partitions = os.path.join(OPTIONS.input_tmp, "META",
                                 "ab_partitions.txt")
    if os.path.exists(ab_partitions):
        with open(ab_partitions, 'r') as f:
            lines = f.readlines()
        # For devices using A/B update, generate care_map for system and vendor
        # partitions (if present), then write this file to target_files package.
        care_map_list = []
        for line in lines:
            if line.strip() == "system" and OPTIONS.info_dict.get(
                    "system_verity_block_device", None) is not None:
                assert os.path.exists(system_imgname)
                care_map_list += GetCareMap("system", system_imgname)
            if line.strip() == "vendor" and OPTIONS.info_dict.get(
                    "vendor_verity_block_device", None) is not None:
                assert os.path.exists(vendor_imgname)
                care_map_list += GetCareMap("vendor", vendor_imgname)

            img_name = line.strip() + ".img"
            img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
            if os.path.exists(img_radio_path):
                common.ZipWrite(output_zip, img_radio_path,
                                os.path.join("IMAGES", img_name))

            # Zip spec says: All slashes MUST be forward slashes.
            img_path = 'IMAGES/' + img_name
            assert img_path in output_zip.namelist(), "cannot find " + img_name

        if care_map_list:
            file_path = "META/care_map.txt"
            common.ZipWriteStr(output_zip, file_path, '\n'.join(care_map_list))

    common.ZipClose(output_zip)
Esempio n. 4
0
def CopyInfo(output_zip):
  """Copy the android-info.txt file from the input to the output."""
  common.ZipWrite(
      output_zip, os.path.join(OPTIONS.input_tmp, "OTA", "android-info.txt"),
      "android-info.txt")
def AddImagesToTargetFiles(filename):
    OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)

    if not OPTIONS.add_missing:
        for n in input_zip.namelist():
            if n.startswith("IMAGES/"):
                print "target_files appears to already contain images."
                sys.exit(1)

    try:
        input_zip.getinfo("VENDOR/")
        has_vendor = True
    except KeyError:
        has_vendor = False

    OPTIONS.info_dict = common.LoadInfoDict(input_zip)
    if "selinux_fc" in OPTIONS.info_dict:
        OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp,
                                                       "BOOT", "RAMDISK",
                                                       "file_contexts")

    common.ZipClose(input_zip)
    output_zip = zipfile.ZipFile(filename,
                                 "a",
                                 compression=zipfile.ZIP_DEFLATED)

    fstab = OPTIONS.info_dict["fstab"]
    if fstab:
        ubi_fs = (fstab["/system"].fs_type == "ubifs")
    else:
        ubi_fs = False

    def banner(s):
        print "\n\n++++ " + s + " ++++\n\n"

    banner("boot")
    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
    boot_image = None
    if os.path.exists(prebuilt_path):
        print "boot.img already exists in IMAGES/, no need to rebuild..."
        if OPTIONS.rebuild_recovery:
            boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                                 OPTIONS.input_tmp, "BOOT")
    else:
        boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                             OPTIONS.input_tmp, "BOOT")
        if boot_image:
            boot_image.AddToZip(output_zip)
            # overlay boot image in product out folder
            if outpath:
                open(outpath + '/boot.img', 'w').write(boot_image.data)
                os.chmod(
                    outpath + '/boot.img',
                    stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)

    banner("recovery")
    recovery_image = None
    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
    if os.path.exists(prebuilt_path):
        print "recovery.img already exists in IMAGES/, no need to rebuild..."
        if OPTIONS.rebuild_recovery:
            recovery_image = common.GetBootableImage("IMAGES/recovery.img",
                                                     "recovery.img",
                                                     OPTIONS.input_tmp,
                                                     "RECOVERY")
    else:
        recovery_image = common.GetBootableImage("IMAGES/recovery.img",
                                                 "recovery.img",
                                                 OPTIONS.input_tmp, "RECOVERY")
        if recovery_image:
            recovery_image.AddToZip(output_zip)
            # overlay recovery image in product out folder
            if outpath:
                open(outpath + '/recovery.img', 'w').write(recovery_image.data)
                os.chmod(
                    outpath + '/recovery.img',
                    stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
    if not ubi_fs:
        banner("system")
        AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image)
    if has_vendor:
        banner("vendor")
        AddVendor(output_zip)
    #banner("userdata")
    #AddUserdata(output_zip)
    #banner("cache")
    #AddCache(output_zip)

    if outpath:
        zip_dir(OPTIONS.input_tmp, 'META', outpath, 'oem_fota_meta')
        zip_dir(OPTIONS.input_tmp, 'OTA', outpath, 'oem_fota_meta')
        if ubi_fs:
            zip_dir(OPTIONS.input_tmp, 'SYSTEM', outpath, 'oem_fota_meta')
        zf = zipfile.ZipFile(outpath + '/oem_fota_meta.zip', "a",
                             zipfile.zlib.DEFLATED)
        if not ubi_fs:
            common.ZipWrite(zf, outpath + '/system.map', '/IMAGES/system.map')
            common.ZipWrite(zf, OPTIONS.input_tmp + '/SYSTEM/build.prop',
                            '/SYSTEM/build.prop')
            common.ZipWrite(zf,
                            OPTIONS.input_tmp + '/SYSTEM/recovery-from-boot.p',
                            '/SYSTEM/recovery-from-boot.p')
            common.ZipWrite(
                zf, OPTIONS.input_tmp + '/SYSTEM/bin/install-recovery.sh',
                '/SYSTEM/bin/install-recovery.sh')
        common.ZipWrite(zf, OPTIONS.input_tmp + '/RADIO/filesmap',
                        '/RADIO/filesmap')
        zf.close()
    common.ZipClose(output_zip)
Esempio n. 6
0
def AddImagesToTargetFiles(filename):
    if os.path.isdir(filename):
        OPTIONS.input_tmp = os.path.abspath(filename)
        input_zip = None
    else:
        OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)

    if not OPTIONS.add_missing:
        if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
            print("target_files appears to already contain images.")
            sys.exit(1)

    # vendor.img is unlike system.img or system_other.img. Because it could be
    # built from source, or dropped into target_files.zip as a prebuilt blob. We
    # consider either of them as vendor.img being available, which could be used
    # when generating vbmeta.img for AVB.
    has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR"))
                  or os.path.exists(
                      os.path.join(OPTIONS.input_tmp, "IMAGES", "vendor.img")))
    has_system_other = os.path.isdir(
        os.path.join(OPTIONS.input_tmp, "SYSTEM_OTHER"))

    if input_zip:
        OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

        common.ZipClose(input_zip)
        output_zip = zipfile.ZipFile(filename,
                                     "a",
                                     compression=zipfile.ZIP_DEFLATED,
                                     allowZip64=True)
    else:
        OPTIONS.info_dict = common.LoadInfoDict(filename, filename)
        output_zip = None
        images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
        if not os.path.isdir(images_dir):
            os.makedirs(images_dir)
        images_dir = None

    has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")

    if OPTIONS.info_dict.get("avb_enable") == "true":
        fp = None
        if "build.prop" in OPTIONS.info_dict:
            build_prop = OPTIONS.info_dict["build.prop"]
            if "ro.build.fingerprint" in build_prop:
                fp = build_prop["ro.build.fingerprint"]
            elif "ro.build.thumbprint" in build_prop:
                fp = build_prop["ro.build.thumbprint"]
        if fp:
            OPTIONS.info_dict["avb_salt"] = hashlib.sha256(fp).hexdigest()

    def banner(s):
        print("\n\n++++ " + s + " ++++\n\n")

    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
    boot_image = None
    if os.path.exists(prebuilt_path):
        banner("boot")
        print("boot.img already exists in IMAGES/, no need to rebuild...")
        if OPTIONS.rebuild_recovery:
            boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                                 OPTIONS.input_tmp, "BOOT")
    else:
        banner("boot")
        boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                             OPTIONS.input_tmp, "BOOT")
        if boot_image:
            if output_zip:
                boot_image.AddToZip(output_zip)
            else:
                boot_image.WriteToDir(OPTIONS.input_tmp)

    recovery_image = None
    if has_recovery:
        banner("recovery")
        prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES",
                                     "recovery.img")
        if os.path.exists(prebuilt_path):
            print(
                "recovery.img already exists in IMAGES/, no need to rebuild..."
            )
            if OPTIONS.rebuild_recovery:
                recovery_image = common.GetBootableImage(
                    "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
                    "RECOVERY")
        else:
            recovery_image = common.GetBootableImage("IMAGES/recovery.img",
                                                     "recovery.img",
                                                     OPTIONS.input_tmp,
                                                     "RECOVERY")
            if recovery_image:
                if output_zip:
                    recovery_image.AddToZip(output_zip)
                else:
                    recovery_image.WriteToDir(OPTIONS.input_tmp)

            banner("recovery (two-step image)")
            # The special recovery.img for two-step package use.
            recovery_two_step_image = common.GetBootableImage(
                "IMAGES/recovery-two-step.img",
                "recovery-two-step.img",
                OPTIONS.input_tmp,
                "RECOVERY",
                two_step_image=True)
            if recovery_two_step_image:
                if output_zip:
                    recovery_two_step_image.AddToZip(output_zip)
                else:
                    recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)

    banner("system")
    system_img_path = AddSystem(output_zip,
                                recovery_img=recovery_image,
                                boot_img=boot_image)
    vendor_img_path = None
    if has_vendor:
        banner("vendor")
        vendor_img_path = AddVendor(output_zip)
    if has_system_other:
        banner("system_other")
        AddSystemOther(output_zip)
    if not OPTIONS.is_signing:
        banner("userdata")
        AddUserdata(output_zip)

    if OPTIONS.info_dict.get("board_bpt_enable") == "true":
        banner("partition-table")
        AddPartitionTable(output_zip)

    dtbo_img_path = None
    if OPTIONS.info_dict.get("has_dtbo") == "true":
        banner("dtbo")
        dtbo_img_path = AddDtbo(output_zip)

    if OPTIONS.info_dict.get("avb_enable") == "true":
        banner("vbmeta")
        boot_contents = boot_image.WriteToTemp()
        AddVBMeta(output_zip, boot_contents.name, system_img_path,
                  vendor_img_path, dtbo_img_path)

    # For devices using A/B update, copy over images from RADIO/ and/or
    # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
    # images ready under IMAGES/. All images should have '.img' as extension.
    banner("radio")
    ab_partitions = os.path.join(OPTIONS.input_tmp, "META",
                                 "ab_partitions.txt")
    if os.path.exists(ab_partitions):
        with open(ab_partitions, 'r') as f:
            lines = f.readlines()
        # For devices using A/B update, generate care_map for system and vendor
        # partitions (if present), then write this file to target_files package.
        care_map_list = []
        for line in lines:
            if line.strip() == "system" and (
                    "system_verity_block_device" in OPTIONS.info_dict
                    or OPTIONS.info_dict.get("avb_system_hashtree_enable")
                    == "true"):
                assert os.path.exists(system_img_path)
                care_map_list += GetCareMap("system", system_img_path)
            if line.strip() == "vendor" and (
                    "vendor_verity_block_device" in OPTIONS.info_dict
                    or OPTIONS.info_dict.get("avb_vendor_hashtree_enable")
                    == "true"):
                assert os.path.exists(vendor_img_path)
                care_map_list += GetCareMap("vendor", vendor_img_path)

            img_name = line.strip() + ".img"
            prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
            if os.path.exists(prebuilt_path):
                print("%s already exists, no need to overwrite..." %
                      (img_name, ))
                continue

            img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
            img_vendor_dir = os.path.join(OPTIONS.input_tmp, "VENDOR_IMAGES")
            if os.path.exists(img_radio_path):
                if output_zip:
                    common.ZipWrite(output_zip, img_radio_path,
                                    os.path.join("IMAGES", img_name))
                else:
                    shutil.copy(img_radio_path, prebuilt_path)
            else:
                for root, _, files in os.walk(img_vendor_dir):
                    if img_name in files:
                        if output_zip:
                            common.ZipWrite(output_zip,
                                            os.path.join(root, img_name),
                                            os.path.join("IMAGES", img_name))
                        else:
                            shutil.copy(os.path.join(root, img_name),
                                        prebuilt_path)
                        break

            if output_zip:
                # Zip spec says: All slashes MUST be forward slashes.
                img_path = 'IMAGES/' + img_name
                assert img_path in output_zip.namelist(
                ), "cannot find " + img_name
            else:
                img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
                assert os.path.exists(img_path), "cannot find " + img_name

        if care_map_list:
            care_map_path = "META/care_map.txt"
            if output_zip and care_map_path not in output_zip.namelist():
                common.ZipWriteStr(output_zip, care_map_path,
                                   '\n'.join(care_map_list))
            else:
                with open(os.path.join(OPTIONS.input_tmp, care_map_path),
                          'w') as fp:
                    fp.write('\n'.join(care_map_list))
                if output_zip:
                    OPTIONS.replace_updated_files_list.append(care_map_path)

    # Radio images that need to be packed into IMAGES/, and product-img.zip.
    pack_radioimages = os.path.join(OPTIONS.input_tmp, "META",
                                    "pack_radioimages.txt")
    if os.path.exists(pack_radioimages):
        with open(pack_radioimages, 'r') as f:
            lines = f.readlines()
        for line in lines:
            img_name = line.strip()
            _, ext = os.path.splitext(img_name)
            if not ext:
                img_name += ".img"
            prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
            if os.path.exists(prebuilt_path):
                print("%s already exists, no need to overwrite..." %
                      (img_name, ))
                continue

            img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
            assert os.path.exists(img_radio_path), \
                "Failed to find %s at %s" % (img_name, img_radio_path)
            if output_zip:
                common.ZipWrite(output_zip, img_radio_path,
                                os.path.join("IMAGES", img_name))
            else:
                shutil.copy(img_radio_path, prebuilt_path)

    if output_zip:
        common.ZipClose(output_zip)
        if OPTIONS.replace_updated_files_list:
            ReplaceUpdatedFiles(output_zip.filename,
                                OPTIONS.replace_updated_files_list)
Esempio n. 7
0
def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
    try:
        keylist = input_tf_zip.read("META/otakeys.txt").split()
    except KeyError:
        raise common.ExternalError("can't read META/otakeys.txt from input")

    extra_recovery_keys = misc_info.get("extra_recovery_keys", None)
    if extra_recovery_keys:
        extra_recovery_keys = [
            OPTIONS.key_map.get(k, k) + ".x509.pem"
            for k in extra_recovery_keys.split()
        ]
        if extra_recovery_keys:
            print "extra recovery-only key(s): " + ", ".join(
                extra_recovery_keys)
    else:
        extra_recovery_keys = []

    mapped_keys = []
    for k in keylist:
        m = re.match(r"^(.*)\.x509\.pem$", k)
        if not m:
            raise common.ExternalError(
                "can't parse \"%s\" from META/otakeys.txt" % (k, ))
        k = m.group(1)
        mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")

    if mapped_keys:
        print "using:\n   ", "\n   ".join(mapped_keys)
        print "for OTA package verification"
    else:
        devkey = misc_info.get("default_system_dev_certificate",
                               "build/target/product/security/testkey")
        mapped_keys.append(OPTIONS.key_map.get(devkey, devkey) + ".x509.pem")
        print(
            "META/otakeys.txt has no keys; using %s for OTA package"
            " verification." % (mapped_keys[0], ))

    # recovery uses a version of the key that has been slightly
    # predigested (by DumpPublicKey.java) and put in res/keys.
    # extra_recovery_keys are used only in recovery.
    cmd = ([OPTIONS.java_path] + OPTIONS.java_args + [
        "-jar",
        os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")
    ] + mapped_keys + extra_recovery_keys)
    p = common.Run(cmd, stdout=subprocess.PIPE)
    new_recovery_keys, _ = p.communicate()
    if p.returncode != 0:
        raise common.ExternalError("failed to run dumpkeys")

    # system_root_image puts the recovery keys at BOOT/RAMDISK.
    if misc_info.get("system_root_image") == "true":
        recovery_keys_location = "BOOT/RAMDISK/res/keys"
    else:
        recovery_keys_location = "RECOVERY/RAMDISK/res/keys"
    common.ZipWriteStr(output_tf_zip, recovery_keys_location,
                       new_recovery_keys)

    # Save the base64 key representation in the update for key-change
    # validations
    p = common.Run(
        ["python", "vendor/dot/build/tools/getb64key.py", mapped_keys[0]],
        stdout=subprocess.PIPE)
    data, _ = p.communicate()
    if p.returncode == 0:
        common.ZipWriteStr(output_tf_zip, "META/releasekey.txt", data)

    # SystemUpdateActivity uses the x509.pem version of the keys, but
    # put into a zipfile system/etc/security/otacerts.zip.
    # We DO NOT include the extra_recovery_keys (if any) here.

    temp_file = cStringIO.StringIO()
    certs_zip = zipfile.ZipFile(temp_file, "w")
    for k in mapped_keys:
        common.ZipWrite(certs_zip, k)
    common.ZipClose(certs_zip)
    common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
                       temp_file.getvalue())

    # For A/B devices, update the payload verification key.
    if misc_info.get("ab_update") == "true":
        # Unlike otacerts.zip that may contain multiple keys, we can only specify
        # ONE payload verification key.
        if len(mapped_keys) > 1:
            print(
                "\n  WARNING: Found more than one OTA keys; Using the first one"
                " as payload verification key.\n\n")

        print "Using %s for payload verification." % (mapped_keys[0], )
        cmd = common.Run(
            ["openssl", "x509", "-pubkey", "-noout", "-in", mapped_keys[0]],
            stdout=subprocess.PIPE)
        pubkey, _ = cmd.communicate()
        common.ZipWriteStr(
            output_tf_zip,
            "SYSTEM/etc/update_engine/update-payload-key.pub.pem", pubkey)
        common.ZipWriteStr(
            output_tf_zip,
            "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem",
            pubkey)

    return new_recovery_keys
Esempio n. 8
0
def main(argv):
  bootable_only = [False]
  compression = [zipfile.ZIP_DEFLATED]

  def option_handler(o, _):
    if o in ("-z", "--bootable_zip"):
      bootable_only[0] = True
    elif o in ("-n", "--no_compression"):
      compression[0] = zipfile.ZIP_STORED
    else:
      return False
    return True

  args = common.ParseOptions(argv, __doc__,
                             extra_opts="zn",
                             extra_long_opts=["bootable_zip", "no_compression"],
                             extra_option_handler=option_handler)

  bootable_only = bootable_only[0]
  compression = compression[0]

  if len(args) != 2:
    common.Usage(__doc__)
    sys.exit(1)

  OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
  output_zip = zipfile.ZipFile(args[1], "w", compression)
  CopyInfo(output_zip)

  try:
    done = False
    images_path = os.path.join(OPTIONS.input_tmp, "IMAGES")
    if os.path.exists(images_path):
      # If this is a new target-files, it already contains the images,
      # and all we have to do is copy them to the output zip.
      images = os.listdir(images_path)
      if images:
        for image in images:
          if bootable_only and image not in ("boot.img", "recovery.img"):
            continue
          if not image.endswith(".img"):
            continue
          common.ZipWrite(
              output_zip, os.path.join(images_path, image), image)
        done = True

    if not done:
      # We have an old target-files that doesn't already contain the
      # images, so build them.
      import add_img_to_target_files

      OPTIONS.info_dict = common.LoadInfoDict(input_zip)

      # If this image was originally labelled with SELinux contexts,
      # make sure we also apply the labels in our new image. During
      # building, the "file_contexts" is in the out/ directory tree,
      # but for repacking from target-files.zip it's in the root
      # directory of the ramdisk.
      if "selinux_fc" in OPTIONS.info_dict:
        OPTIONS.info_dict["selinux_fc"] = os.path.join(
            OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts")

      boot_image = common.GetBootableImage(
          "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
      if boot_image:
        boot_image.AddToZip(output_zip)
      recovery_image = common.GetBootableImage(
          "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
      if recovery_image:
        recovery_image.AddToZip(output_zip)

      def banner(s):
        print "\n\n++++ " + s + " ++++\n\n"

      if not bootable_only:
        banner("AddSystem")
        add_img_to_target_files.AddSystem(output_zip, prefix="")
        try:
          input_zip.getinfo("VENDOR/")
          banner("AddVendor")
          add_img_to_target_files.AddVendor(output_zip, prefix="")
        except KeyError:
          pass   # no vendor partition for this device
        banner("AddUserdata")
        add_img_to_target_files.AddUserdata(output_zip, prefix="")
        banner("AddCache")
        add_img_to_target_files.AddCache(output_zip, prefix="")

  finally:
    print "cleaning up..."
    common.ZipClose(output_zip)
    shutil.rmtree(OPTIONS.input_tmp)

  print "done."
Esempio n. 9
0
def AddImagesToTargetFiles(filename):
  OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)

  if not OPTIONS.add_missing:
    for n in input_zip.namelist():
      if n.startswith("IMAGES/"):
        print("target_files appears to already contain images.")
        sys.exit(1)

  try:
    input_zip.getinfo("VENDOR/")
    has_vendor = True
  except KeyError:
    has_vendor = False

  has_system_other = "SYSTEM_OTHER/" in input_zip.namelist()

  OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

  common.ZipClose(input_zip)
  output_zip = zipfile.ZipFile(filename, "a",
                               compression=zipfile.ZIP_DEFLATED,
                               allowZip64=True)

  has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
  system_root_image = (OPTIONS.info_dict.get("system_root_image", None) == "true")

  def banner(s):
    print("\n\n++++ " + s + " ++++\n\n")

  prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
  boot_image = None
  if os.path.exists(prebuilt_path):
    banner("boot")
    print("boot.img already exists in IMAGES/, no need to rebuild...")
    if OPTIONS.rebuild_recovery:
      boot_image = common.GetBootableImage(
          "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
  else:
    banner("boot")
    boot_image = common.GetBootableImage(
        "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
    if boot_image:
      boot_image.AddToZip(output_zip)

  recovery_image = None
  if has_recovery:
    banner("recovery")
    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
    if os.path.exists(prebuilt_path):
      print("recovery.img already exists in IMAGES/, no need to rebuild...")
      if OPTIONS.rebuild_recovery:
        recovery_image = common.GetBootableImage(
            "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
            "RECOVERY")
    else:
      recovery_image = common.GetBootableImage(
          "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
      if recovery_image:
        recovery_image.AddToZip(output_zip)

      banner("recovery (two-step image)")
      # The special recovery.img for two-step package use.
      recovery_two_step_image = common.GetBootableImage(
          "IMAGES/recovery-two-step.img", "recovery-two-step.img",
          OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
      if recovery_two_step_image:
        recovery_two_step_image.AddToZip(output_zip)

  banner("system")
  system_img_path = AddSystem(
    output_zip, recovery_img=recovery_image, boot_img=boot_image)
  vendor_img_path = None
  if has_vendor:
    banner("vendor")
    vendor_img_path = AddVendor(output_zip)
  if has_system_other:
    banner("system_other")
    AddSystemOther(output_zip)
  if not OPTIONS.is_signing:
    banner("userdata")
    AddUserdata(output_zip)
    banner("cache")
    AddCache(output_zip)
  if OPTIONS.info_dict.get("board_bpt_enable", None) == "true":
    banner("partition-table")
    AddPartitionTable(output_zip)
  if OPTIONS.info_dict.get("board_avb_enable", None) == "true":
    banner("vbmeta")
    boot_contents = boot_image.WriteToTemp()
    AddVBMeta(output_zip, boot_contents.name, system_img_path)

  # For devices using A/B update, copy over images from RADIO/ and/or
  # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
  # images ready under IMAGES/. All images should have '.img' as extension.
  banner("radio")
  ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt")
  if os.path.exists(ab_partitions):
    with open(ab_partitions, 'r') as f:
      lines = f.readlines()
    # For devices using A/B update, generate care_map for system and vendor
    # partitions (if present), then write this file to target_files package.
    care_map_list = []
    for line in lines:
      if line.strip() == "system" and OPTIONS.info_dict.get(
          "system_verity_block_device", None) is not None:
        assert os.path.exists(system_img_path)
        care_map_list += GetCareMap("system", system_img_path)
      if line.strip() == "vendor" and OPTIONS.info_dict.get(
          "vendor_verity_block_device", None) is not None:
        assert os.path.exists(vendor_img_path)
        care_map_list += GetCareMap("vendor", vendor_img_path)

      img_name = line.strip() + ".img"
      prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
      if os.path.exists(prebuilt_path):
        print("%s already exists, no need to overwrite..." % (img_name,))
        continue

      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
      img_vendor_dir = os.path.join(
        OPTIONS.input_tmp, "VENDOR_IMAGES")
      if os.path.exists(img_radio_path):
        common.ZipWrite(output_zip, img_radio_path,
                        os.path.join("IMAGES", img_name))
      else:
        for root, _, files in os.walk(img_vendor_dir):
          if img_name in files:
            common.ZipWrite(output_zip, os.path.join(root, img_name),
              os.path.join("IMAGES", img_name))
            break

      # Zip spec says: All slashes MUST be forward slashes.
      img_path = 'IMAGES/' + img_name
      assert img_path in output_zip.namelist(), "cannot find " + img_name

    if care_map_list:
      file_path = "META/care_map.txt"
      common.ZipWriteStr(output_zip, file_path, '\n'.join(care_map_list))

  common.ZipClose(output_zip)
def AddImagesToTargetFiles(filename):
  OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)

  if not OPTIONS.add_missing:
    for n in input_zip.namelist():
      if n.startswith("IMAGES/"):
        print "target_files appears to already contain images."
        sys.exit(1)

  try:
    input_zip.getinfo("VENDOR/")
    has_vendor = True
  except KeyError:
    has_vendor = False

  OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

  common.ZipClose(input_zip)
  output_zip = zipfile.ZipFile(filename, "a",
                               compression=zipfile.ZIP_DEFLATED)

  has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
  system_root_image = (OPTIONS.info_dict.get("system_root_image", None) == "true")
  board_bvb_enable = (OPTIONS.info_dict.get("board_bvb_enable", None) == "true")

  # Brillo Verified Boot is incompatible with certain
  # configurations. Explicitly check for these.
  if board_bvb_enable:
    assert not has_recovery, "has_recovery incompatible with bvb"
    assert not system_root_image, "system_root_image incompatible with bvb"
    assert not OPTIONS.rebuild_recovery, "rebuild_recovery incompatible with bvb"
    assert not has_vendor, "VENDOR images currently incompatible with bvb"

  def banner(s):
    print "\n\n++++ " + s + " ++++\n\n"

  prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
  boot_image = None
  if os.path.exists(prebuilt_path):
    banner("boot")
    print "boot.img already exists in IMAGES/, no need to rebuild..."
    if OPTIONS.rebuild_recovery:
      boot_image = common.GetBootableImage(
          "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
  else:
    if board_bvb_enable:
      # With Brillo Verified Boot, we need to build system.img before
      # boot.img since the latter includes the dm-verity root hash and
      # salt for the former.
      pass
    else:
      banner("boot")
      boot_image = common.GetBootableImage(
        "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
      if boot_image:
        boot_image.AddToZip(output_zip)

  recovery_image = None
  if has_recovery:
    banner("recovery")
    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
    if os.path.exists(prebuilt_path):
      print "recovery.img already exists in IMAGES/, no need to rebuild..."
      if OPTIONS.rebuild_recovery:
        recovery_image = common.GetBootableImage(
            "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
            "RECOVERY")
    else:
      recovery_image = common.GetBootableImage(
          "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
      if recovery_image:
        recovery_image.AddToZip(output_zip)

  banner("system")
  system_img_path = AddSystem(
    output_zip, recovery_img=recovery_image, boot_img=boot_image)
  if OPTIONS.info_dict.get("board_bvb_enable", None) == "true":
    # If we're using Brillo Verified Boot, we can now build boot.img
    # given that we have system.img.
    banner("boot")
    boot_image = common.GetBootableImage(
      "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT",
      system_img_path=system_img_path)
    if boot_image:
      boot_image.AddToZip(output_zip)
  if has_vendor:
    banner("vendor")
    AddVendor(output_zip)
  banner("userdata")
  AddUserdata(output_zip)
  banner("cache")
  AddCache(output_zip)
  if OPTIONS.info_dict.get("board_bpt_enable", None) == "true":
    banner("partition-table")
    AddPartitionTable(output_zip)

  # For devices using A/B update, copy over images from RADIO/ and/or
  # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
  # images ready under IMAGES/. All images should have '.img' as extension.
  ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt")
  if os.path.exists(ab_partitions):
    with open(ab_partitions, 'r') as f:
      lines = f.readlines()
    for line in lines:
      img_name = line.strip() + ".img"
      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
      img_vendor_dir = os.path.join(
        OPTIONS.input_tmp, "VENDOR_IMAGES")
      if os.path.exists(img_radio_path):
        common.ZipWrite(output_zip, img_radio_path,
                        os.path.join("IMAGES", img_name))
      else:
        for root, _, files in os.walk(img_vendor_dir):
          if img_name in files:
            common.ZipWrite(output_zip, os.path.join(root, img_name),
              os.path.join("IMAGES", img_name))
            break

      # Zip spec says: All slashes MUST be forward slashes.
      img_path = 'IMAGES/' + img_name
      assert img_path in output_zip.namelist(), "cannot find " + img_name

  common.ZipClose(output_zip)
def AddImagesToTargetFiles(filename):
  """Creates and adds images (boot/recovery/system/...) to a target_files.zip.

  It works with either a zip file (zip mode), or a directory that contains the
  files to be packed into a target_files.zip (dir mode). The latter is used when
  being called from build/make/core/Makefile.

  The images will be created under IMAGES/ in the input target_files.zip.

  Args:
      filename: the target_files.zip, or the zip root directory.
  """
  if os.path.isdir(filename):
    OPTIONS.input_tmp = os.path.abspath(filename)
    input_zip = None
  else:
    OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)

  if not OPTIONS.add_missing:
    if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
      print("target_files appears to already contain images.")
      sys.exit(1)

  # vendor.img is unlike system.img or system_other.img. Because it could be
  # built from source, or dropped into target_files.zip as a prebuilt blob. We
  # consider either of them as vendor.img being available, which could be used
  # when generating vbmeta.img for AVB.
  has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or
                os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
                                            "vendor.img")))
  has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
                                                "SYSTEM_OTHER"))

  if input_zip:
    OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

    common.ZipClose(input_zip)
    output_zip = zipfile.ZipFile(filename, "a",
                                 compression=zipfile.ZIP_DEFLATED,
                                 allowZip64=True)
  else:
    OPTIONS.info_dict = common.LoadInfoDict(filename, filename)
    output_zip = None

  # Always make input_tmp/IMAGES available, since we may stage boot / recovery
  # images there even under zip mode. The directory will be cleaned up as part
  # of OPTIONS.input_tmp.
  images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
  if not os.path.isdir(images_dir):
    os.makedirs(images_dir)

  has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")

  # A map between partition names and their paths, which could be used when
  # generating AVB vbmeta image.
  partitions = dict()

  def banner(s):
    print("\n\n++++ " + s + " ++++\n\n")

  banner("boot")
  # common.GetBootableImage() returns the image directly if present.
  boot_image = common.GetBootableImage(
      "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
  # boot.img may be unavailable in some targets (e.g. aosp_arm64).
  if boot_image:
    partitions['boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
    if not os.path.exists(partitions['boot']):
      boot_image.WriteToDir(OPTIONS.input_tmp)
      if output_zip:
        boot_image.AddToZip(output_zip)

  recovery_image = None
  if has_recovery:
    banner("recovery")
    recovery_image = common.GetBootableImage(
        "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
    assert recovery_image, "Failed to create recovery.img."
    partitions['recovery'] = os.path.join(
        OPTIONS.input_tmp, "IMAGES", "recovery.img")
    if not os.path.exists(partitions['recovery']):
      recovery_image.WriteToDir(OPTIONS.input_tmp)
      if output_zip:
        recovery_image.AddToZip(output_zip)

      banner("recovery (two-step image)")
      # The special recovery.img for two-step package use.
      recovery_two_step_image = common.GetBootableImage(
          "IMAGES/recovery-two-step.img", "recovery-two-step.img",
          OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
      assert recovery_two_step_image, "Failed to create recovery-two-step.img."
      recovery_two_step_image_path = os.path.join(
          OPTIONS.input_tmp, "IMAGES", "recovery-two-step.img")
      if not os.path.exists(recovery_two_step_image_path):
        recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
        if output_zip:
          recovery_two_step_image.AddToZip(output_zip)

  banner("system")
  partitions['system'] = system_img_path = AddSystem(
      output_zip, recovery_img=recovery_image, boot_img=boot_image)

  if has_vendor:
    banner("vendor")
    partitions['vendor'] = vendor_img_path = AddVendor(output_zip)

  if has_system_other:
    banner("system_other")
    AddSystemOther(output_zip)

  if not OPTIONS.is_signing:
    banner("userdata")
    AddUserdata(output_zip)
    banner("cache")
    AddCache(output_zip)

  if OPTIONS.info_dict.get("board_bpt_enable") == "true":
    banner("partition-table")
    AddPartitionTable(output_zip)

  if OPTIONS.info_dict.get("has_dtbo") == "true":
    banner("dtbo")
    partitions['dtbo'] = AddDtbo(output_zip)

  if OPTIONS.info_dict.get("avb_enable") == "true":
    banner("vbmeta")
    AddVBMeta(output_zip, partitions)

  if OPTIONS.info_dict.get("avb_disabled_vbmeta") == "true":
    banner("vbmeta")
    AddDisabledVBMeta(output_zip)

  # For devices using A/B update, copy over images from RADIO/ and/or
  # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
  # images ready under IMAGES/. All images should have '.img' as extension.
  banner("radio")
  ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt")
  if os.path.exists(ab_partitions):
    with open(ab_partitions, 'r') as f:
      lines = f.readlines()
    # For devices using A/B update, generate care_map for system and vendor
    # partitions (if present), then write this file to target_files package.
    care_map_list = []
    for line in lines:
      if line.strip() == "system" and (
          "system_verity_block_device" in OPTIONS.info_dict or
          OPTIONS.info_dict.get("avb_system_hashtree_enable") == "true"):
        assert os.path.exists(system_img_path)
        care_map_list += GetCareMap("system", system_img_path)
      if line.strip() == "vendor" and (
          "vendor_verity_block_device" in OPTIONS.info_dict or
          OPTIONS.info_dict.get("avb_vendor_hashtree_enable") == "true"):
        assert os.path.exists(vendor_img_path)
        care_map_list += GetCareMap("vendor", vendor_img_path)

      img_name = line.strip() + ".img"
      prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
      if os.path.exists(prebuilt_path):
        print("%s already exists, no need to overwrite..." % (img_name,))
        continue

      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
      img_vendor_dir = os.path.join(
        OPTIONS.input_tmp, "VENDOR_IMAGES")
      if os.path.exists(img_radio_path):
        if output_zip:
          common.ZipWrite(output_zip, img_radio_path,
                          os.path.join("IMAGES", img_name))
        else:
          shutil.copy(img_radio_path, prebuilt_path)
      else:
        for root, _, files in os.walk(img_vendor_dir):
          if img_name in files:
            if output_zip:
              common.ZipWrite(output_zip, os.path.join(root, img_name),
                os.path.join("IMAGES", img_name))
            else:
              shutil.copy(os.path.join(root, img_name), prebuilt_path)
            break

      if output_zip:
        # Zip spec says: All slashes MUST be forward slashes.
        img_path = 'IMAGES/' + img_name
        assert img_path in output_zip.namelist(), "cannot find " + img_name
      else:
        img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
        assert os.path.exists(img_path), "cannot find " + img_name

    if care_map_list:
      care_map_path = "META/care_map.txt"
      if output_zip and care_map_path not in output_zip.namelist():
        common.ZipWriteStr(output_zip, care_map_path, '\n'.join(care_map_list))
      else:
        with open(os.path.join(OPTIONS.input_tmp, care_map_path), 'w') as fp:
          fp.write('\n'.join(care_map_list))
        if output_zip:
          OPTIONS.replace_updated_files_list.append(care_map_path)

  # Radio images that need to be packed into IMAGES/, and product-img.zip.
  pack_radioimages = os.path.join(
      OPTIONS.input_tmp, "META", "pack_radioimages.txt")
  if os.path.exists(pack_radioimages):
    with open(pack_radioimages, 'r') as f:
      lines = f.readlines()
    for line in lines:
      img_name = line.strip()
      _, ext = os.path.splitext(img_name)
      if not ext:
        img_name += ".img"
      prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
      if os.path.exists(prebuilt_path):
        print("%s already exists, no need to overwrite..." % (img_name,))
        continue

      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
      assert os.path.exists(img_radio_path), \
          "Failed to find %s at %s" % (img_name, img_radio_path)
      if output_zip:
        common.ZipWrite(output_zip, img_radio_path,
                        os.path.join("IMAGES", img_name))
      else:
        shutil.copy(img_radio_path, prebuilt_path)

  if output_zip:
    common.ZipClose(output_zip)
    if OPTIONS.replace_updated_files_list:
      ReplaceUpdatedFiles(output_zip.filename,
                          OPTIONS.replace_updated_files_list)
def AddImagesToTargetFiles(filename):
    OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)

    if not OPTIONS.add_missing:
        for n in input_zip.namelist():
            if n.startswith("IMAGES/"):
                print "target_files appears to already contain images."
                sys.exit(1)

    try:
        input_zip.getinfo("VENDOR/")
        has_vendor = True
    except KeyError:
        has_vendor = False

    OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

    common.ZipClose(input_zip)
    output_zip = zipfile.ZipFile(filename,
                                 "a",
                                 compression=zipfile.ZIP_DEFLATED)

    has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")

    def banner(s):
        print "\n\n++++ " + s + " ++++\n\n"

    banner("boot")
    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
    boot_image = None
    if os.path.exists(prebuilt_path):
        print "boot.img already exists in IMAGES/, no need to rebuild..."
        if OPTIONS.rebuild_recovery:
            boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                                 OPTIONS.input_tmp, "BOOT")
    else:
        boot_image = common.GetBootableImage("IMAGES/boot.img", "boot.img",
                                             OPTIONS.input_tmp, "BOOT")
        if boot_image:
            boot_image.AddToZip(output_zip)

    recovery_image = None
    if has_recovery:
        banner("recovery")
        prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES",
                                     "recovery.img")
        if os.path.exists(prebuilt_path):
            print "recovery.img already exists in IMAGES/, no need to rebuild..."
            if OPTIONS.rebuild_recovery:
                recovery_image = common.GetBootableImage(
                    "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
                    "RECOVERY")
        else:
            recovery_image = common.GetBootableImage("IMAGES/recovery.img",
                                                     "recovery.img",
                                                     OPTIONS.input_tmp,
                                                     "RECOVERY")
            if recovery_image:
                recovery_image.AddToZip(output_zip)

    banner("system")
    AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image)
    if has_vendor:
        banner("vendor")
        AddVendor(output_zip)
    if not OPTIONS.is_signing:
        banner("userdata")
        AddUserdata(output_zip)
        banner("cache")
        AddCache(output_zip)
    if OPTIONS.info_dict.get("board_bpt_enable", None) == "true":
        banner("partition-table")
        AddPartitionTable(output_zip)

    # For devices using A/B update, copy over images from RADIO/ and/or
    # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
    # images ready under IMAGES/. All images should have '.img' as extension.
    banner("radio")
    ab_partitions = os.path.join(OPTIONS.input_tmp, "META",
                                 "ab_partitions.txt")
    if os.path.exists(ab_partitions):
        with open(ab_partitions, 'r') as f:
            lines = f.readlines()
        for line in lines:
            img_name = line.strip() + ".img"
            img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
            if os.path.exists(img_radio_path):
                common.ZipWrite(output_zip, img_radio_path,
                                os.path.join("IMAGES", img_name))

            # Zip spec says: All slashes MUST be forward slashes.
            img_path = 'IMAGES/' + img_name
            assert img_path in output_zip.namelist(), "cannot find " + img_name

    common.ZipClose(output_zip)
Esempio n. 13
0
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, apk_keys,
                       apex_keys, key_passwords, platform_api_level,
                       codename_to_api_level_map, compressed_extension):
    # maxsize measures the maximum filename length, including the ones to be
    # skipped.
    maxsize = max([
        len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
        if GetApkFileInfo(i.filename, compressed_extension, [])[0]
    ])
    system_root_image = misc_info.get("system_root_image") == "true"

    # A dict of APEX payload public keys that should be updated, i.e. the files
    # under '/system/etc/security/apex/'.
    updated_apex_payload_keys = {}

    for info in input_tf_zip.infolist():
        filename = info.filename
        if filename.startswith("IMAGES/"):
            continue

        # Skip split super images, which will be re-generated during signing.
        if filename.startswith("OTA/") and filename.endswith(".img"):
            continue

        data = input_tf_zip.read(filename)
        out_info = copy.copy(info)
        (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
            filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)

        if is_apk and should_be_skipped:
            # Copy skipped APKs verbatim.
            print("NOT signing: %s\n"
                  "        (skipped due to matching prefix)" % (filename, ))
            common.ZipWriteStr(output_tf_zip, out_info, data)

        # Sign APKs.
        elif is_apk:
            name = os.path.basename(filename)
            if is_compressed:
                name = name[:-len(compressed_extension)]

            key = apk_keys[name]
            if key not in common.SPECIAL_CERT_STRINGS:
                print("    signing: %-*s (%s)" % (maxsize, name, key))
                signed_data = SignApk(data, key, key_passwords[key],
                                      platform_api_level,
                                      codename_to_api_level_map, is_compressed)
                common.ZipWriteStr(output_tf_zip, out_info, signed_data)
            else:
                # an APK we're not supposed to sign.
                print("NOT signing: %s\n"
                      "        (skipped due to special cert string)" %
                      (name, ))
                common.ZipWriteStr(output_tf_zip, out_info, data)

        # Sign bundled APEX files.
        elif filename.startswith("SYSTEM/apex") and filename.endswith(".apex"):
            name = os.path.basename(filename)
            payload_key, container_key = apex_keys[name]

            print("    signing: %-*s container (%s)" %
                  (maxsize, name, container_key))
            print("           : %-*s payload   (%s)" %
                  (maxsize, name, payload_key))

            (signed_apex,
             payload_key_name) = SignApex(data, payload_key, container_key,
                                          key_passwords[container_key],
                                          codename_to_api_level_map,
                                          OPTIONS.avb_extra_args.get('apex'))
            common.ZipWrite(output_tf_zip, signed_apex, filename)

            updated_apex_payload_keys[payload_key_name] = payload_key

        # AVB public keys for the installed APEXes, which will be updated later.
        elif (os.path.dirname(filename) == 'SYSTEM/etc/security/apex'
              and filename != 'SYSTEM/etc/security/apex/'):
            continue

        # System properties.
        elif filename in (
                "SYSTEM/build.prop",
                "VENDOR/build.prop",
                "SYSTEM/etc/prop.default",
                "BOOT/RAMDISK/prop.default",
                "BOOT/RAMDISK/default.prop",  # legacy
                "ROOT/default.prop",  # legacy
                "RECOVERY/RAMDISK/prop.default",
                "RECOVERY/RAMDISK/default.prop"):  # legacy
            print("Rewriting %s:" % (filename, ))
            if stat.S_ISLNK(info.external_attr >> 16):
                new_data = data
            else:
                new_data = RewriteProps(data)
            common.ZipWriteStr(output_tf_zip, out_info, new_data)

        # Replace the certs in *mac_permissions.xml (there could be multiple, such
        # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
        elif filename.endswith("mac_permissions.xml"):
            print("Rewriting %s with new keys." % (filename, ))
            new_data = ReplaceCerts(data)
            common.ZipWriteStr(output_tf_zip, out_info, new_data)

        # Ask add_img_to_target_files to rebuild the recovery patch if needed.
        elif filename in ("SYSTEM/recovery-from-boot.p",
                          "SYSTEM/etc/recovery.img",
                          "SYSTEM/bin/install-recovery.sh"):
            OPTIONS.rebuild_recovery = True

        # Don't copy OTA certs if we're replacing them.
        elif (
                OPTIONS.replace_ota_keys and filename in
            ("BOOT/RAMDISK/system/etc/security/otacerts.zip",
             "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem",
             "RECOVERY/RAMDISK/system/etc/security/otacerts.zip",
             "SYSTEM/etc/security/otacerts.zip",
             "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
            pass

        # Skip META/misc_info.txt since we will write back the new values later.
        elif filename == "META/misc_info.txt":
            pass

        # Skip verity public key if we will replace it.
        elif (OPTIONS.replace_verity_public_key
              and filename in ("BOOT/RAMDISK/verity_key", "ROOT/verity_key")):
            pass

        # Skip verity keyid (for system_root_image use) if we will replace it.
        elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
            pass

        # Skip the care_map as we will regenerate the system/vendor images.
        elif filename == "META/care_map.pb" or filename == "META/care_map.txt":
            pass

        # A non-APK file; copy it verbatim.
        else:
            common.ZipWriteStr(output_tf_zip, out_info, data)

    # Update APEX payload public keys.
    for info in input_tf_zip.infolist():
        filename = info.filename
        if (os.path.dirname(filename) != 'SYSTEM/etc/security/apex'
                or filename == 'SYSTEM/etc/security/apex/'):
            continue

        name = os.path.basename(filename)
        assert name in updated_apex_payload_keys, \
            'Unsigned APEX payload key: {}'.format(filename)

        key_path = updated_apex_payload_keys[name]
        if not os.path.exists(key_path) and not key_path.endswith('.pem'):
            key_path = '{}.pem'.format(key_path)
        assert os.path.exists(key_path), \
            'Failed to find public key file {} for APEX {}'.format(
                updated_apex_payload_keys[name], name)

        print('Replacing APEX payload public key for {} with {}'.format(
            name, key_path))

        public_key = common.ExtractAvbPublicKey(key_path)
        common.ZipWrite(output_tf_zip, public_key, arcname=filename)

    if OPTIONS.replace_ota_keys:
        ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)

    # Replace the keyid string in misc_info dict.
    if OPTIONS.replace_verity_private_key:
        ReplaceVerityPrivateKey(misc_info,
                                OPTIONS.replace_verity_private_key[1])

    if OPTIONS.replace_verity_public_key:
        dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key"
        # We are replacing the one in boot image only, since the one under
        # recovery won't ever be needed.
        ReplaceVerityPublicKey(output_tf_zip, dest,
                               OPTIONS.replace_verity_public_key[1])

    # Replace the keyid string in BOOT/cmdline.
    if OPTIONS.replace_verity_keyid:
        ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
                           OPTIONS.replace_verity_keyid[1])

    # Replace the AVB signing keys, if any.
    ReplaceAvbSigningKeys(misc_info)

    # Write back misc_info with the latest values.
    ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
Esempio n. 14
0
def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info,
                       apk_keys, apex_keys, key_passwords,
                       platform_api_level, codename_to_api_level_map,
                       compressed_extension):
  # maxsize measures the maximum filename length, including the ones to be
  # skipped.
  maxsize = max(
      [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
       if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
  system_root_image = misc_info.get("system_root_image") == "true"

  for info in input_tf_zip.infolist():
    filename = info.filename
    if filename.startswith("IMAGES/"):
      continue

    # Skip OTA-specific images (e.g. split super images), which will be
    # re-generated during signing.
    if filename.startswith("OTA/") and filename.endswith(".img"):
      continue

    data = input_tf_zip.read(filename)
    out_info = copy.copy(info)
    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
        filename, compressed_extension, OPTIONS.skip_apks_with_path_prefix)

    if is_apk and should_be_skipped:
      # Copy skipped APKs verbatim.
      print(
          "NOT signing: %s\n"
          "        (skipped due to matching prefix)" % (filename,))
      common.ZipWriteStr(output_tf_zip, out_info, data)

    # Sign APKs.
    elif is_apk:
      name = os.path.basename(filename)
      if is_compressed:
        name = name[:-len(compressed_extension)]

      key = apk_keys[name]
      if key not in common.SPECIAL_CERT_STRINGS:
        print("    signing: %-*s (%s)" % (maxsize, name, key))
        signed_data = SignApk(data, key, key_passwords[key], platform_api_level,
                              codename_to_api_level_map, is_compressed, name)
        common.ZipWriteStr(output_tf_zip, out_info, signed_data)
      else:
        # an APK we're not supposed to sign.
        print(
            "NOT signing: %s\n"
            "        (skipped due to special cert string)" % (name,))
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # Sign bundled APEX files on all partitions
    elif filename.endswith(".apex"):
      name = os.path.basename(filename)
      payload_key, container_key = apex_keys[name]

      # We've asserted not having a case with only one of them PRESIGNED.
      if (payload_key not in common.SPECIAL_CERT_STRINGS and
          container_key not in common.SPECIAL_CERT_STRINGS):
        print("    signing: %-*s container (%s)" % (
            maxsize, name, container_key))
        print("           : %-*s payload   (%s)" % (
            maxsize, name, payload_key))

        signed_apex = apex_utils.SignApex(
            misc_info['avb_avbtool'],
            data,
            payload_key,
            container_key,
            key_passwords,
            apk_keys,
            codename_to_api_level_map,
            no_hashtree=True,
            signing_args=OPTIONS.avb_extra_args.get('apex'))
        common.ZipWrite(output_tf_zip, signed_apex, filename)

      else:
        print(
            "NOT signing: %s\n"
            "        (skipped due to special cert string)" % (name,))
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # System properties.
    elif IsBuildPropFile(filename):
      print("Rewriting %s:" % (filename,))
      if stat.S_ISLNK(info.external_attr >> 16):
        new_data = data
      else:
        new_data = RewriteProps(data.decode())
      common.ZipWriteStr(output_tf_zip, out_info, new_data)

    # Replace the certs in *mac_permissions.xml (there could be multiple, such
    # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
    elif filename.endswith("mac_permissions.xml"):
      print("Rewriting %s with new keys." % (filename,))
      new_data = ReplaceCerts(data.decode())
      common.ZipWriteStr(output_tf_zip, out_info, new_data)

    # Ask add_img_to_target_files to rebuild the recovery patch if needed.
    elif filename in ("SYSTEM/recovery-from-boot.p",
                      "VENDOR/recovery-from-boot.p",

                      "SYSTEM/etc/recovery.img",
                      "VENDOR/etc/recovery.img",

                      "SYSTEM/bin/install-recovery.sh",
                      "VENDOR/bin/install-recovery.sh"):
      OPTIONS.rebuild_recovery = True

    # Don't copy OTA certs if we're replacing them.
    # Replacement of update-payload-key.pub.pem was removed in b/116660991.
    elif OPTIONS.replace_ota_keys and filename.endswith("/otacerts.zip"):
      pass

    # Skip META/misc_info.txt since we will write back the new values later.
    elif filename == "META/misc_info.txt":
      pass

    # Skip verity public key if we will replace it.
    elif (OPTIONS.replace_verity_public_key and
          filename in ("BOOT/RAMDISK/verity_key",
                       "ROOT/verity_key")):
      pass
    elif (OPTIONS.remove_avb_public_keys and
          (filename.startswith("BOOT/RAMDISK/avb/") or
           filename.startswith("BOOT/RAMDISK/first_stage_ramdisk/avb/"))):
      matched_removal = False
      for key_to_remove in OPTIONS.remove_avb_public_keys:
        if filename.endswith(key_to_remove):
          matched_removal = True
          print("Removing AVB public key from ramdisk: %s" % filename)
          break
      if not matched_removal:
        # Copy it verbatim if we don't want to remove it.
        common.ZipWriteStr(output_tf_zip, out_info, data)

    # Skip verity keyid (for system_root_image use) if we will replace it.
    elif OPTIONS.replace_verity_keyid and filename == "BOOT/cmdline":
      pass

    # Skip the care_map as we will regenerate the system/vendor images.
    elif filename in ["META/care_map.pb", "META/care_map.txt"]:
      pass

    # Skip apex_info.pb because we sign/modify apexes
    elif filename == "META/apex_info.pb":
      pass

    # Updates system_other.avbpubkey in /product/etc/.
    elif filename in (
        "PRODUCT/etc/security/avb/system_other.avbpubkey",
        "SYSTEM/product/etc/security/avb/system_other.avbpubkey"):
      # Only update system_other's public key, if the corresponding signing
      # key is specified via --avb_system_other_key.
      signing_key = OPTIONS.avb_keys.get("system_other")
      if signing_key:
        public_key = common.ExtractAvbPublicKey(
            misc_info['avb_avbtool'], signing_key)
        print("    Rewriting AVB public key of system_other in /product")
        common.ZipWrite(output_tf_zip, public_key, filename)

    # Should NOT sign boot-debug.img.
    elif filename in (
        "BOOT/RAMDISK/force_debuggable",
        "BOOT/RAMDISK/first_stage_ramdisk/force_debuggable"):
      raise common.ExternalError("debuggable boot.img cannot be signed")

    # A non-APK file; copy it verbatim.
    else:
      common.ZipWriteStr(output_tf_zip, out_info, data)

  if OPTIONS.replace_ota_keys:
    ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info)

  # Replace the keyid string in misc_info dict.
  if OPTIONS.replace_verity_private_key:
    ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1])

  if OPTIONS.replace_verity_public_key:
    # Replace the one in root dir in system.img.
    ReplaceVerityPublicKey(
        output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1])

    if not system_root_image:
      # Additionally replace the copy in ramdisk if not using system-as-root.
      ReplaceVerityPublicKey(
          output_tf_zip,
          'BOOT/RAMDISK/verity_key',
          OPTIONS.replace_verity_public_key[1])

  # Replace the keyid string in BOOT/cmdline.
  if OPTIONS.replace_verity_keyid:
    ReplaceVerityKeyId(input_tf_zip, output_tf_zip,
                       OPTIONS.replace_verity_keyid[1])

  # Replace the AVB signing keys, if any.
  ReplaceAvbSigningKeys(misc_info)

  # Rewrite the props in AVB signing args.
  if misc_info.get('avb_enable') == 'true':
    RewriteAvbProps(misc_info)

  # Write back misc_info with the latest values.
  ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
def ReplaceVerityPublicKey(targetfile_zip, filename, key_path):
  print "Replacing verity public key with %s" % (key_path,)
  common.ZipWrite(targetfile_zip, key_path, arcname=filename)
def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
    """Returns a target-files.zip file for generating secondary payload.

  Although the original target-files.zip already contains secondary slot
  images (i.e. IMAGES/system_other.img), we need to rename the files to the
  ones without _other suffix. Note that we cannot instead modify the names in
  META/ab_partitions.txt, because there are no matching partitions on device.

  For the partitions that don't have secondary images, the ones for primary
  slot will be used. This is to ensure that we always have valid boot, vbmeta,
  bootloader images in the inactive slot.

  Args:
    input_file: The input target-files.zip file.
    skip_postinstall: Whether to skip copying the postinstall config file.

  Returns:
    The filename of the target-files.zip for generating secondary payload.
  """
    def GetInfoForSecondaryImages(info_file):
        """Updates info file for secondary payload generation.

    Scan each line in the info file, and remove the unwanted partitions from
    the dynamic partition list in the related properties. e.g.
    "super_google_dynamic_partitions_partition_list=system vendor product"
    will become "super_google_dynamic_partitions_partition_list=system".

    Args:
      info_file: The input info file. e.g. misc_info.txt.

    Returns:
      A string of the updated info content.
    """

        output_list = []
        with open(info_file) as f:
            lines = f.read().splitlines()

        # The suffix in partition_list variables that follows the name of the
        # partition group.
        LIST_SUFFIX = 'partition_list'
        for line in lines:
            if line.startswith('#') or '=' not in line:
                output_list.append(line)
                continue
            key, value = line.strip().split('=', 1)
            if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX):
                partitions = value.split()
                partitions = [
                    partition for partition in partitions
                    if partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES
                ]
                output_list.append('{}={}'.format(key, ' '.join(partitions)))
            elif key in ['virtual_ab', "virtual_ab_retrofit"]:
                # Remove virtual_ab flag from secondary payload so that OTA client
                # don't use snapshots for secondary update
                pass
            else:
                output_list.append(line)
        return '\n'.join(output_list)

    target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
    target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)

    with zipfile.ZipFile(input_file, 'r') as input_zip:
        infolist = input_zip.infolist()

    input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
    for info in infolist:
        unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
        if info.filename == 'IMAGES/system_other.img':
            common.ZipWrite(target_zip,
                            unzipped_file,
                            arcname='IMAGES/system.img')

        # Primary images and friends need to be skipped explicitly.
        elif info.filename in ('IMAGES/system.img', 'IMAGES/system.map'):
            pass

        # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
        elif info.filename.startswith(('IMAGES/', 'RADIO/')):
            image_name = os.path.basename(info.filename)
            if image_name not in [
                    '{}.img'.format(partition)
                    for partition in SECONDARY_PAYLOAD_SKIPPED_IMAGES
            ]:
                common.ZipWrite(target_zip,
                                unzipped_file,
                                arcname=info.filename)

        # Skip copying the postinstall config if requested.
        elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
            pass

        elif info.filename.startswith('META/'):
            # Remove the unnecessary partitions for secondary images from the
            # ab_partitions file.
            if info.filename == AB_PARTITIONS:
                with open(unzipped_file) as f:
                    partition_list = f.read().splitlines()
                partition_list = [
                    partition for partition in partition_list if partition
                    and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES
                ]
                common.ZipWriteStr(target_zip, info.filename,
                                   '\n'.join(partition_list))
            # Remove the unnecessary partitions from the dynamic partitions list.
            elif (info.filename == 'META/misc_info.txt'
                  or info.filename == DYNAMIC_PARTITION_INFO):
                modified_info = GetInfoForSecondaryImages(unzipped_file)
                common.ZipWriteStr(target_zip, info.filename, modified_info)
            else:
                common.ZipWrite(target_zip,
                                unzipped_file,
                                arcname=info.filename)

    common.ZipClose(target_zip)

    return target_file
def AddCareMapForAbOta(output_zip, ab_partitions, image_paths):
    """Generates and adds care_map.pb for a/b partition that has care_map.

  Args:
    output_zip: The output zip file (needs to be already open), or None to
        write care_map.pb to OPTIONS.input_tmp/.
    ab_partitions: The list of A/B partitions.
    image_paths: A map from the partition name to the image path.
  """
    care_map_list = []
    for partition in ab_partitions:
        partition = partition.strip()
        if partition not in common.PARTITIONS_WITH_CARE_MAP:
            continue

        verity_block_device = "{}_verity_block_device".format(partition)
        avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition)
        if (verity_block_device in OPTIONS.info_dict
                or OPTIONS.info_dict.get(avb_hashtree_enable) == "true"):
            image_path = image_paths[partition]
            assert os.path.exists(image_path)

            care_map = GetCareMap(partition, image_path)
            if not care_map:
                continue
            care_map_list += care_map

            # adds fingerprint field to the care_map
            # TODO(xunchang) revisit the fingerprint calculation for care_map.
            partition_props = OPTIONS.info_dict.get(partition + ".build.prop")
            prop_name_list = [
                "ro.{}.build.fingerprint".format(partition),
                "ro.{}.build.thumbprint".format(partition)
            ]

            present_props = [
                x for x in prop_name_list
                if partition_props and partition_props.GetProp(x)
            ]
            if not present_props:
                logger.warning("fingerprint is not present for partition %s",
                               partition)
                property_id, fingerprint = "unknown", "unknown"
            else:
                property_id = present_props[0]
                fingerprint = partition_props.GetProp(property_id)
            care_map_list += [property_id, fingerprint]

    if not care_map_list:
        return

    # Converts the list into proto buf message by calling care_map_generator; and
    # writes the result to a temp file.
    temp_care_map_text = common.MakeTempFile(prefix="caremap_text-",
                                             suffix=".txt")
    with open(temp_care_map_text, 'w') as text_file:
        text_file.write('\n'.join(care_map_list))

    temp_care_map = common.MakeTempFile(prefix="caremap-", suffix=".pb")
    care_map_gen_cmd = [
        "care_map_generator", temp_care_map_text, temp_care_map
    ]
    common.RunAndCheckOutput(care_map_gen_cmd)

    care_map_path = "META/care_map.pb"
    if output_zip and care_map_path not in output_zip.namelist():
        common.ZipWrite(output_zip, temp_care_map, arcname=care_map_path)
    else:
        shutil.copy(temp_care_map,
                    os.path.join(OPTIONS.input_tmp, care_map_path))
        if output_zip:
            OPTIONS.replace_updated_files_list.append(care_map_path)
def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
                                                  super_block_devices,
                                                  dynamic_partition_list):
    """Returns a target-files.zip for retrofitting dynamic partitions.

  This allows brillo_update_payload to generate an OTA based on the exact
  bits on the block devices. Postinstall is disabled.

  Args:
    input_file: The input target-files.zip filename.
    super_block_devices: The list of super block devices
    dynamic_partition_list: The list of dynamic partitions

  Returns:
    The filename of target-files.zip with *.img replaced with super_*.img for
    each block device in super_block_devices.
  """
    assert super_block_devices, "No super_block_devices are specified."

    replace = {
        'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
        for dev in super_block_devices
    }

    target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
    shutil.copyfile(input_file, target_file)

    with zipfile.ZipFile(input_file) as input_zip:
        namelist = input_zip.namelist()

    input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)

    # Remove partitions from META/ab_partitions.txt that is in
    # dynamic_partition_list but not in super_block_devices so that
    # brillo_update_payload won't generate update for those logical partitions.
    ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
    with open(ab_partitions_file) as f:
        ab_partitions_lines = f.readlines()
        ab_partitions = [line.strip() for line in ab_partitions_lines]
    # Assert that all super_block_devices are in ab_partitions
    super_device_not_updated = [
        partition for partition in super_block_devices
        if partition not in ab_partitions
    ]
    assert not super_device_not_updated, \
        "{} is in super_block_devices but not in {}".format(
            super_device_not_updated, AB_PARTITIONS)
    # ab_partitions -= (dynamic_partition_list - super_block_devices)
    new_ab_partitions = common.MakeTempFile(prefix="ab_partitions",
                                            suffix=".txt")
    with open(new_ab_partitions, 'w') as f:
        for partition in ab_partitions:
            if (partition in dynamic_partition_list
                    and partition not in super_block_devices):
                logger.info("Dropping %s from ab_partitions.txt", partition)
                continue
            f.write(partition + "\n")
    to_delete = [AB_PARTITIONS]

    # Always skip postinstall for a retrofit update.
    to_delete += [POSTINSTALL_CONFIG]

    # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
    # is a regular update on devices without dynamic partitions support.
    to_delete += [DYNAMIC_PARTITION_INFO]

    # Remove the existing partition images as well as the map files.
    to_delete += list(replace.values())
    to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]

    common.ZipDelete(target_file, to_delete)

    target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)

    # Write super_{foo}.img as {foo}.img.
    for src, dst in replace.items():
        assert src in namelist, \
            'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
        unzipped_file = os.path.join(input_tmp, *src.split('/'))
        common.ZipWrite(target_zip, unzipped_file, arcname=dst)

    # Write new ab_partitions.txt file
    common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)

    common.ZipClose(target_zip)

    return target_file
Esempio n. 19
0
 def Write(self):
     if self._output_zip:
         common.ZipWrite(self._output_zip, self.name, self._zip_name)
Esempio n. 20
0
      # and all we have to do is copy them to the output zip.
      # Skip oem.img files since they are not needed in fastboot images.
      images = os.listdir(images_path)
      if images:
        for image in images:
          if bootable_only and image not in ("boot.img", "recovery.img"):
            continue
          if not image.endswith(".img"):
            continue
<<<<<<< HEAD
          if i == "oem.img":
=======
          if image == "recovery-two-step.img":
>>>>>>> android-7.1.2_r2
            continue
          common.ZipWrite(
              output_zip, os.path.join(images_path, image), image)
        done = True

    if not done:
      # We have an old target-files that doesn't already contain the
      # images, so build them.
      import add_img_to_target_files

      OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

      boot_image = common.GetBootableImage(
          "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
      if boot_image:
        boot_image.AddToZip(output_zip)

      if OPTIONS.info_dict.get("no_recovery") != "true":
Esempio n. 21
0
def SignApex(avbtool,
             apex_data,
             payload_key,
             container_key,
             container_pw,
             apk_keys,
             codename_to_api_level_map,
             no_hashtree,
             signing_args=None):
    """Signs the current APEX with the given payload/container keys.

  Args:
    apex_data: Raw APEX data.
    payload_key: The path to payload signing key (w/ extension).
    container_key: The path to container signing key (w/o extension).
    container_pw: The matching password of the container_key, or None.
    apk_keys: A dict that holds the signing keys for apk files.
    codename_to_api_level_map: A dict that maps from codename to API level.
    no_hashtree: Don't include hashtree in the signed APEX.
    signing_args: Additional args to be passed to the payload signer.

  Returns:
    The path to the signed APEX file.
  """
    apex_file = common.MakeTempFile(prefix='apex-', suffix='.apex')
    with open(apex_file, 'wb') as apex_fp:
        apex_fp.write(apex_data)

    APEX_PUBKEY = 'apex_pubkey'

    # 1. Extract the apex payload image and sign the containing apk files. Repack
    # the apex file after signing.
    apk_signer = ApexApkSigner(apex_file, container_pw,
                               codename_to_api_level_map)
    apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key, signing_args)

    # 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
    # payload_key.
    payload_dir = common.MakeTempDir(prefix='apex-payload-')
    with zipfile.ZipFile(apex_file) as apex_fd:
        payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
        zip_items = apex_fd.namelist()

    payload_info = ParseApexPayloadInfo(avbtool, payload_file)
    SignApexPayload(avbtool, payload_file, payload_key,
                    payload_info['apex.key'], payload_info['Algorithm'],
                    payload_info['Salt'], payload_info['Hash Algorithm'],
                    no_hashtree, signing_args)

    # 2b. Update the embedded payload public key.
    payload_public_key = common.ExtractAvbPublicKey(avbtool, payload_key)
    common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
    if APEX_PUBKEY in zip_items:
        common.ZipDelete(apex_file, APEX_PUBKEY)
    apex_zip = zipfile.ZipFile(apex_file, 'a')
    common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
    common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
    common.ZipClose(apex_zip)

    # 3. Align the files at page boundary (same as in apexer).
    aligned_apex = common.MakeTempFile(prefix='apex-container-',
                                       suffix='.apex')
    common.RunAndCheckOutput(
        ['zipalign', '-f', '4096', apex_file, aligned_apex])

    # 4. Sign the APEX container with container_key.
    signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')

    # Specify the 4K alignment when calling SignApk.
    extra_signapk_args = OPTIONS.extra_signapk_args[:]
    extra_signapk_args.extend(['-a', '4096'])

    common.SignFile(aligned_apex,
                    signed_apex,
                    container_key,
                    container_pw.get(container_key),
                    codename_to_api_level_map=codename_to_api_level_map,
                    extra_signapk_args=extra_signapk_args)

    return signed_apex
Esempio n. 22
0
def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info):
  try:
    keylist = input_tf_zip.read("META/otakeys.txt").split()
  except KeyError:
    raise common.ExternalError("can't read META/otakeys.txt from input")

  extra_recovery_keys = misc_info.get("extra_recovery_keys", None)
  if extra_recovery_keys:
    extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
                           for k in extra_recovery_keys.split()]
    if extra_recovery_keys:
      print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
  else:
    extra_recovery_keys = []

  mapped_keys = []
  for k in keylist:
    m = re.match(r"^(.*)\.x509\.pem$", k)
    if not m:
      raise common.ExternalError(
          "can't parse \"%s\" from META/otakeys.txt" % (k,))
    k = m.group(1)
    mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem")

  if mapped_keys:
    print("using:\n   ", "\n   ".join(mapped_keys))
    print("for OTA package verification")
  else:
    devkey = misc_info.get("default_system_dev_certificate",
                           "build/target/product/security/testkey")
    mapped_keys.append(
        OPTIONS.key_map.get(devkey, devkey) + ".x509.pem")
    print("META/otakeys.txt has no keys; using", mapped_keys[0])

  # recovery uses a version of the key that has been slightly
  # predigested (by DumpPublicKey.java) and put in res/keys.
  # extra_recovery_keys are used only in recovery.

  p = common.Run(["java", "-jar",
                  os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar")]
                 + mapped_keys + extra_recovery_keys,
                 stdout=subprocess.PIPE)
  new_recovery_keys, _ = p.communicate()
  if p.returncode != 0:
    raise common.ExternalError("failed to run dumpkeys")
  common.ZipWriteStr(output_tf_zip, "RECOVERY/RAMDISK/res/keys",
                     new_recovery_keys)

  # Save the base64 key representation in the update for key-change
  # validations
  p = common.Run(["python", "build/tools/getb64key.py", mapped_keys[0]],
                 stdout=subprocess.PIPE)
  data, _ = p.communicate()
  if p.returncode == 0:
    common.ZipWriteStr(output_tf_zip, "META/releasekey.txt", data)

  # SystemUpdateActivity uses the x509.pem version of the keys, but
  # put into a zipfile system/etc/security/otacerts.zip.
  # We DO NOT include the extra_recovery_keys (if any) here.

  temp_file = StringIO()
  certs_zip = zipfile.ZipFile(temp_file, "w")
  for k in mapped_keys:
    common.ZipWrite(certs_zip, k)
  common.ZipClose(certs_zip)
  common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip",
                     temp_file.getvalue())

  return new_recovery_keys
Esempio n. 23
0
def main(argv):
  bootable_only = [False]

  def option_handler(o, _):
    if o in ("-z", "--bootable_zip"):
      bootable_only[0] = True
    else:
      return False
    return True

  args = common.ParseOptions(argv, __doc__,
                             extra_opts="z",
                             extra_long_opts=["bootable_zip"],
                             extra_option_handler=option_handler)

  bootable_only = bootable_only[0]

  if len(args) != 2:
    common.Usage(__doc__)
    sys.exit(1)

  OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
  output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
  CopyInfo(output_zip)

  try:
    done = False
    images_path = os.path.join(OPTIONS.input_tmp, "IMAGES")
    if os.path.exists(images_path):
      # If this is a new target-files, it already contains the images,
      # and all we have to do is copy them to the output zip.
      images = os.listdir(images_path)
      if images:
        for image in images:
          if bootable_only and image not in ("boot.img", "recovery.img"):
            continue
          if not image.endswith(".img"):
            continue
          common.ZipWrite(
              output_zip, os.path.join(images_path, image), image)
        done = True

    if not done:
      # We have an old target-files that doesn't already contain the
      # images, so build them.
      import add_img_to_target_files

      OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)

      boot_image = common.GetBootableImage(
          "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
      if boot_image:
        boot_image.AddToZip(output_zip)
      recovery_image = common.GetBootableImage(
          "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
      if recovery_image:
        recovery_image.AddToZip(output_zip)

      def banner(s):
        print "\n\n++++ " + s + " ++++\n\n"

      if not bootable_only:
        banner("AddSystem")
        add_img_to_target_files.AddSystem(output_zip, prefix="")
        try:
          input_zip.getinfo("VENDOR/")
          banner("AddVendor")
          add_img_to_target_files.AddVendor(output_zip, prefix="")
        except KeyError:
          pass   # no vendor partition for this device
        banner("AddUserdata")
        add_img_to_target_files.AddUserdata(output_zip, prefix="")
        banner("AddCache")
        add_img_to_target_files.AddCache(output_zip, prefix="")

  finally:
    print "cleaning up..."
    common.ZipClose(output_zip)
    shutil.rmtree(OPTIONS.input_tmp)

  print "done."
Esempio n. 24
0
def SignUncompressedApex(avbtool,
                         apex_file,
                         payload_key,
                         container_key,
                         container_pw,
                         apk_keys,
                         codename_to_api_level_map,
                         no_hashtree,
                         signing_args=None,
                         sign_tool=None):
    """Signs the current uncompressed APEX with the given payload/container keys.

  Args:
    apex_file: Uncompressed APEX file.
    payload_key: The path to payload signing key (w/ extension).
    container_key: The path to container signing key (w/o extension).
    container_pw: The matching password of the container_key, or None.
    apk_keys: A dict that holds the signing keys for apk files.
    codename_to_api_level_map: A dict that maps from codename to API level.
    no_hashtree: Don't include hashtree in the signed APEX.
    signing_args: Additional args to be passed to the payload signer.
    sign_tool: A tool to sign the contents of the APEX.

  Returns:
    The path to the signed APEX file.
  """
    # 1. Extract the apex payload image and sign the files (e.g. APKs). Repack
    # the apex file after signing.
    apk_signer = ApexApkSigner(apex_file, container_pw,
                               codename_to_api_level_map, avbtool, sign_tool)
    apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key, signing_args)

    # 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
    # payload_key.
    payload_dir = common.MakeTempDir(prefix='apex-payload-')
    with zipfile.ZipFile(apex_file) as apex_fd:
        payload_file = apex_fd.extract(APEX_PAYLOAD_IMAGE, payload_dir)
        zip_items = apex_fd.namelist()

    payload_info = ParseApexPayloadInfo(avbtool, payload_file)
    if no_hashtree is None:
        no_hashtree = payload_info.get("Tree Size", 0) == 0
    SignApexPayload(avbtool, payload_file, payload_key,
                    payload_info['apex.key'], payload_info['Algorithm'],
                    payload_info['Salt'], payload_info['Hash Algorithm'],
                    no_hashtree, signing_args)

    # 2b. Update the embedded payload public key.
    payload_public_key = common.ExtractAvbPublicKey(avbtool, payload_key)
    common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
    if APEX_PUBKEY in zip_items:
        common.ZipDelete(apex_file, APEX_PUBKEY)
    apex_zip = zipfile.ZipFile(apex_file, 'a', allowZip64=True)
    common.ZipWrite(apex_zip, payload_file, arcname=APEX_PAYLOAD_IMAGE)
    common.ZipWrite(apex_zip, payload_public_key, arcname=APEX_PUBKEY)
    common.ZipClose(apex_zip)

    # 3. Sign the APEX container with container_key.
    signed_apex = common.MakeTempFile(prefix='apex-container-', suffix='.apex')

    # Specify the 4K alignment when calling SignApk.
    extra_signapk_args = OPTIONS.extra_signapk_args[:]
    extra_signapk_args.extend(['-a', '4096', '--align-file-size'])

    password = container_pw.get(container_key) if container_pw else None
    common.SignFile(apex_file,
                    signed_apex,
                    container_key,
                    password,
                    codename_to_api_level_map=codename_to_api_level_map,
                    extra_signapk_args=extra_signapk_args)

    return signed_apex