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)
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)
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)
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
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."
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)
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)
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
def Write(self): if self._output_zip: common.ZipWrite(self._output_zip, self.name, self._zip_name)
# 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":
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
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
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."
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