Merge "Fix tests using unaligned pointers"
diff --git a/apex/Android.bp b/apex/Android.bp
index dce8edd..bdea039 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -3,15 +3,8 @@
 }
 
 microdroid_filesystem_images = [
-    "microdroid_boot",
-    "microdroid_bootconfig_debuggable",
-    "microdroid_bootconfig_normal",
-    "microdroid_init_boot",
     "microdroid_super",
-    "microdroid_uboot_env",
     "microdroid_vbmeta",
-    "microdroid_vbmeta_bootconfig",
-    "microdroid_vendor_boot",
 ]
 
 soong_config_module_type {
@@ -98,8 +91,6 @@
         "microdroid_initrd_debuggable",
         "microdroid_initrd_normal",
         "microdroid.json",
-        "microdroid_bootloader",
-        "microdroid_bootloader.avbpubkey",
         "microdroid_kernel",
     ],
     host_required: [
@@ -146,7 +137,10 @@
         },
     },
     required: [
+        // sign_virt_apex should be runnable from outside the source tree,
+        // therefore, any required tool should be listed in build/make/core/Makefile as well.
         "img2simg",
+        "initrd_bootconfig",
         "lpmake",
         "lpunpack",
         "simg2img",
@@ -167,6 +161,7 @@
         // sign_virt_apex
         "avbtool",
         "img2simg",
+        "initrd_bootconfig",
         "lpmake",
         "lpunpack",
         "sign_virt_apex",
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 557c8aa..65e8414 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -24,7 +24,7 @@
 
 sign_virt_apex uses external tools which are assumed to be available via PATH.
 - avbtool (--avbtool can override the tool)
-- lpmake, lpunpack, simg2img, img2simg
+- lpmake, lpunpack, simg2img, img2simg, initrd_bootconfig
 """
 import argparse
 import hashlib
@@ -102,6 +102,11 @@
     parser.add_argument(
         'input_dir',
         help='the directory having files to be packaged')
+    parser.add_argument(
+        '--do_not_update_bootconfigs',
+        action='store_true',
+        help='This will NOT update the vbmeta related bootconfigs while signing the apex.\
+            Used for testing only!!')
     args = parser.parse_args(argv)
     # preprocess --key_override into a map
     args.key_overrides = {}
@@ -254,6 +259,82 @@
         RunCommand(args, cmd)
 
 
+def UpdateVbmetaBootconfig(args, initrds, vbmeta_img):
+    # Update the bootconfigs in ramdisk
+    def detach_bootconfigs(initrd_bc, initrd, bc):
+        cmd = ['initrd_bootconfig', 'detach', initrd_bc, initrd, bc]
+        RunCommand(args, cmd)
+
+    def attach_bootconfigs(initrd_bc, initrd, bc):
+        cmd = ['initrd_bootconfig', 'attach',
+               initrd, bc, '--output', initrd_bc]
+        RunCommand(args, cmd)
+
+    # Validate that avb version used while signing the apex is the same as used by build server
+    def validate_avb_version(bootconfigs):
+        cmd = ['avbtool', 'version']
+        stdout, _ = RunCommand(args, cmd)
+        avb_version_curr = stdout.split(" ")[1].strip()
+        avb_version_curr = avb_version_curr[0:avb_version_curr.rfind('.')]
+
+        avb_version_bc = re.search(
+            r"androidboot.vbmeta.avb_version = \"([^\"]*)\"", bootconfigs).group(1)
+        if avb_version_curr != avb_version_bc:
+            raise Exception(f'AVB version mismatch between current & one & \
+                used to build bootconfigs:{avb_version_curr}&{avb_version_bc}')
+
+    def calc_vbmeta_digest():
+        cmd = ['avbtool', 'calculate_vbmeta_digest', '--image',
+               vbmeta_img, '--hash_algorithm', 'sha256']
+        stdout, _ = RunCommand(args, cmd)
+        return stdout.strip()
+
+    def calc_vbmeta_size():
+        cmd = ['avbtool', 'info_image', '--image', vbmeta_img]
+        stdout, _ = RunCommand(args, cmd)
+        size = 0
+        for line in stdout.split("\n"):
+            line = line.split(":")
+            if line[0] in ['Header Block', 'Authentication Block', 'Auxiliary Block']:
+                size += int(line[1].strip()[0:-6])
+        return size
+
+    def update_vbmeta_digest(bootconfigs):
+        # Update androidboot.vbmeta.digest in bootconfigs
+        result = re.search(
+            r"androidboot.vbmeta.digest = \"[^\"]*\"", bootconfigs)
+        if not result:
+            raise ValueError("Failed to find androidboot.vbmeta.digest")
+
+        return bootconfigs.replace(result.group(),
+                                   f'androidboot.vbmeta.digest = "{calc_vbmeta_digest()}"')
+
+    def update_vbmeta_size(bootconfigs):
+        # Update androidboot.vbmeta.size in bootconfigs
+        result = re.search(r"androidboot.vbmeta.size = [0-9]+", bootconfigs)
+        if not result:
+            raise ValueError("Failed to find androidboot.vbmeta.size")
+        return bootconfigs.replace(result.group(),
+                                   f'androidboot.vbmeta.size = {calc_vbmeta_size()}')
+
+    with tempfile.TemporaryDirectory() as work_dir:
+        tmp_initrd = os.path.join(work_dir, 'initrd')
+        tmp_bc = os.path.join(work_dir, 'bc')
+
+        for initrd in initrds:
+            detach_bootconfigs(initrd, tmp_initrd, tmp_bc)
+            bc_file = open(tmp_bc, "rt", encoding="utf-8")
+            bc_data = bc_file.read()
+            validate_avb_version(bc_data)
+            bc_data = update_vbmeta_digest(bc_data)
+            bc_data = update_vbmeta_size(bc_data)
+            bc_file.close()
+            bc_file = open(tmp_bc, "wt", encoding="utf-8")
+            bc_file.write(bc_data)
+            bc_file.flush()
+            attach_bootconfigs(initrd, tmp_initrd, tmp_bc)
+
+
 def MakeVbmetaImage(args, key, vbmeta_img, images=None, chained_partitions=None):
     if os.path.basename(vbmeta_img) in args.key_overrides:
         key = args.key_overrides[os.path.basename(vbmeta_img)]
@@ -318,43 +399,13 @@
         RunCommand(args, cmd)
 
 
-def ReplaceBootloaderPubkey(args, key, bootloader, bootloader_pubkey):
-    if os.path.basename(bootloader) in args.key_overrides:
-        key = args.key_overrides[os.path.basename(bootloader)]
-    # read old pubkey before replacement
-    with open(bootloader_pubkey, 'rb') as f:
-        old_pubkey = f.read()
-
-    # replace bootloader pubkey (overwrite the old one with the new one)
-    ExtractAvbPubkey(args, key, bootloader_pubkey)
-
-    # read new pubkey
-    with open(bootloader_pubkey, 'rb') as f:
-        new_pubkey = f.read()
-
-    assert len(old_pubkey) == len(new_pubkey)
-
-    # replace pubkey embedded in bootloader
-    with open(bootloader, 'r+b') as bl_f:
-        pos = bl_f.read().find(old_pubkey)
-        assert pos != -1
-        bl_f.seek(pos)
-        bl_f.write(new_pubkey)
-
-
 # dict of (key, file) for re-sign/verification. keys are un-versioned for readability.
 virt_apex_files = {
-    'bootloader.pubkey': 'etc/microdroid_bootloader.avbpubkey',
-    'bootloader': 'etc/fs/microdroid_bootloader',
-    'boot.img': 'etc/fs/microdroid_boot.img',
-    'vendor_boot.img': 'etc/fs/microdroid_vendor_boot.img',
-    'init_boot.img': 'etc/fs/microdroid_init_boot.img',
-    'super.img': 'etc/fs/microdroid_super.img',
+    'kernel': 'etc/fs/microdroid_kernel',
     'vbmeta.img': 'etc/fs/microdroid_vbmeta.img',
-    'vbmeta_bootconfig.img': 'etc/fs/microdroid_vbmeta_bootconfig.img',
-    'bootconfig.normal': 'etc/fs/microdroid_bootconfig.normal',
-    'bootconfig.debuggable': 'etc/fs/microdroid_bootconfig.debuggable',
-    'uboot_env.img': 'etc/fs/uboot_env.img'
+    'super.img': 'etc/fs/microdroid_super.img',
+    'initrd_normal.img': 'etc/microdroid_initrd_normal.img',
+    'initrd_debuggable.img': 'etc/microdroid_initrd_debuggable.img',
 }
 
 
@@ -371,17 +422,6 @@
     system_a_img = os.path.join(unpack_dir.name, 'system_a.img')
     vendor_a_img = os.path.join(unpack_dir.name, 'vendor_a.img')
 
-    # Key(pubkey) embedded in bootloader should match with the one used to make VBmeta below
-    # while it's okay to use different keys for other image files.
-    replace_f = Async(ReplaceBootloaderPubkey, args,
-                      key, files['bootloader'], files['bootloader.pubkey'])
-
-    # re-sign bootloader, boot.img, vendor_boot.img, and init_boot.img
-    Async(AddHashFooter, args, key, files['bootloader'], wait=[replace_f])
-    Async(AddHashFooter, args, key, files['boot.img'])
-    Async(AddHashFooter, args, key, files['vendor_boot.img'])
-    Async(AddHashFooter, args, key, files['init_boot.img'])
-
     # re-sign super.img
     # 1. unpack super.img
     # 2. resign system and vendor
@@ -390,27 +430,22 @@
     system_a_f = Async(AddHashTreeFooter, args, key, system_a_img)
     vendor_a_f = Async(AddHashTreeFooter, args, key, vendor_a_img)
     partitions = {"system_a": system_a_img, "vendor_a": vendor_a_img}
-    Async(MakeSuperImage, args, partitions, files['super.img'], wait=[system_a_f, vendor_a_f])
+    Async(MakeSuperImage, args, partitions,
+          files['super.img'], wait=[system_a_f, vendor_a_f])
 
     # re-generate vbmeta from re-signed {system_a, vendor_a}.img
-    Async(MakeVbmetaImage, args, key, files['vbmeta.img'],
-          images=[system_a_img, vendor_a_img],
-          wait=[system_a_f, vendor_a_f])
+    vbmeta_f = Async(MakeVbmetaImage, args, key, files['vbmeta.img'],
+                     images=[system_a_img, vendor_a_img],
+                     wait=[system_a_f, vendor_a_f])
 
-    # Re-sign bootconfigs and the uboot_env with the same key
-    bootconfig_sign_key = key
-    Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.normal'])
-    Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.debuggable'])
-    Async(AddHashFooter, args, bootconfig_sign_key, files['uboot_env.img'])
+    if not args.do_not_update_bootconfigs:
+        Async(UpdateVbmetaBootconfig, args, [files['initrd_normal.img'],
+                                             files['initrd_debuggable.img']], files['vbmeta.img'],
+              wait=[vbmeta_f])
 
-    # Re-sign vbmeta_bootconfig with chained_partitions to "bootconfig" and
-    # "uboot_env". Note that, for now, `key` and `bootconfig_sign_key` are the
-    # same, but technically they can be different. Vbmeta records pubkeys which
-    # signed chained partitions.
-    Async(MakeVbmetaImage, args, key, files['vbmeta_bootconfig.img'], chained_partitions={
-        'bootconfig': bootconfig_sign_key,
-        'uboot_env': bootconfig_sign_key,
-    })
+    # Re-sign kernel
+    # TODO(b/265382249): Kernel's vbmeta should contain hashes of initrd
+    Async(AddHashFooter, args, key, files['kernel'])
 
 
 def VerifyVirtApex(args):
@@ -430,27 +465,16 @@
             pubkey = f.read()
             pubkey_digest = hashlib.sha1(pubkey).hexdigest()
 
-    def contents(file):
-        with open(file, 'rb') as f:
-            return f.read()
-
-    def check_equals_pubkey(file):
-        assert contents(file) == pubkey, f'pubkey mismatch: {file}'
-
-    def check_contains_pubkey(file):
-        assert contents(file).find(pubkey) != -1, f'pubkey missing: {file}'
-
     def check_avb_pubkey(file):
         info, _ = AvbInfo(args, file)
         assert info is not None, f'no avbinfo: {file}'
         assert info['Public key (sha1)'] == pubkey_digest, f'pubkey mismatch: {file}'
 
     for f in files.values():
-        if f == files['bootloader.pubkey']:
-            Async(check_equals_pubkey, f)
-        elif f == files['bootloader']:
-            Async(check_contains_pubkey, f)
-        elif f == files['super.img']:
+        if f in (files['initrd_normal.img'], files['initrd_debuggable.img']):
+            # TODO(b/245277660): Verify that ramdisks contain the correct vbmeta digest
+            continue
+        if f == files['super.img']:
             Async(check_avb_pubkey, system_a_img)
             Async(check_avb_pubkey, vendor_a_img)
         else:
@@ -467,7 +491,7 @@
             SignVirtApex(args)
         # ensure all tasks are completed without exceptions
         AwaitAll(tasks)
-    except: # pylint: disable=bare-except
+    except:  # pylint: disable=bare-except
         traceback.print_exc()
         sys.exit(1)
 
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 529686d..9264692 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -240,37 +240,6 @@
     ],
 }
 
-bootimg {
-    name: "microdroid_boot",
-    // We don't have kernel for arm and x86. But Soong demands one when it builds for
-    // arm or x86 target. Satisfy that by providing an empty file as the kernel.
-    kernel_prebuilt: "empty_kernel",
-    arch: {
-        arm64: {
-            kernel_prebuilt: ":microdroid_kernel_prebuilts-6.1-arm64",
-        },
-        x86_64: {
-            kernel_prebuilt: ":microdroid_kernel_prebuilts-6.1-x86_64",
-        },
-    },
-
-    dtb_prebuilt: "dummy_dtb.img",
-    header_version: "4",
-    partition_name: "boot",
-    use_avb: true,
-    avb_private_key: ":microdroid_sign_key",
-}
-
-bootimg {
-    name: "microdroid_init_boot",
-    ramdisk_module: "microdroid_ramdisk",
-    kernel_prebuilt: "empty_kernel",
-    header_version: "4",
-    partition_name: "init_boot",
-    use_avb: true,
-    avb_private_key: ":microdroid_sign_key",
-}
-
 android_filesystem {
     name: "microdroid_ramdisk",
     deps: [
@@ -289,25 +258,6 @@
     type: "compressed_cpio",
 }
 
-bootimg {
-    name: "microdroid_vendor_boot",
-    ramdisk_module: "microdroid_vendor_ramdisk",
-    dtb_prebuilt: "dummy_dtb.img",
-    header_version: "4",
-    vendor_boot: true,
-    arch: {
-        arm64: {
-            bootconfig: ":microdroid_bootconfig_arm64_gen",
-        },
-        x86_64: {
-            bootconfig: ":microdroid_bootconfig_x86_64_gen",
-        },
-    },
-    partition_name: "vendor_boot",
-    use_avb: true,
-    avb_private_key: ":microdroid_sign_key",
-}
-
 android_filesystem {
     name: "microdroid_vendor_ramdisk",
     deps: [
@@ -347,43 +297,6 @@
     cmd: "cat $(in) > $(out)",
 }
 
-vbmeta {
-    name: "microdroid_vbmeta_bootconfig",
-    partition_name: "vbmeta",
-    private_key: ":microdroid_sign_key",
-    chained_partitions: [
-        {
-            name: "bootconfig",
-            private_key: ":microdroid_sign_key",
-        },
-        {
-            name: "uboot_env",
-            private_key: ":microdroid_sign_key",
-        },
-    ],
-}
-
-// python -c "import hashlib; print(hashlib.sha256(b'bootconfig').hexdigest())"
-bootconfig_salt = "e158851fbebb402e1f18ea9372ea2f76b4dea23eceb5c4b92e5b27ade8537f5b"
-
-avb_add_hash_footer {
-    name: "microdroid_bootconfig_normal",
-    src: "bootconfig.normal",
-    filename: "microdroid_bootconfig.normal",
-    partition_name: "bootconfig",
-    private_key: ":microdroid_sign_key",
-    salt: bootconfig_salt,
-}
-
-avb_add_hash_footer {
-    name: "microdroid_bootconfig_debuggable",
-    src: "bootconfig.debuggable",
-    filename: "microdroid_bootconfig.debuggable",
-    partition_name: "bootconfig",
-    private_key: ":microdroid_sign_key",
-    salt: bootconfig_salt,
-}
-
 prebuilt_etc {
     name: "microdroid_fstab",
     src: "fstab.microdroid",
@@ -391,89 +304,9 @@
     installable: false,
 }
 
-prebuilt_etc {
-    name: "microdroid_bootloader",
-    src: ":microdroid_bootloader_signed",
-    arch: {
-        x86_64: {
-            // For unknown reason, the signed bootloader doesn't work on x86_64. Until the problem
-            // is fixed, let's use the unsigned bootloader for the architecture.
-            // TODO(b/185115783): remove this
-            src: ":microdroid_bootloader_pubkey_replaced",
-        },
-    },
-    relative_install_path: "fs",
-    filename: "microdroid_bootloader",
-}
-
 // python -c "import hashlib; print(hashlib.sha256(b'bootloader').hexdigest())"
 bootloader_salt = "3b4a12881d11f33cff968a24d7c53723a8232cde9a8d91e29fdbd6a95ae6adf0"
 
-avb_add_hash_footer {
-    name: "microdroid_bootloader_signed",
-    src: ":microdroid_bootloader_pubkey_replaced",
-    filename: "microdroid_bootloader",
-    partition_name: "bootloader",
-    private_key: ":microdroid_sign_key",
-    salt: bootloader_salt,
-}
-
-// Replace avbpubkey of prebuilt bootloader with the avbpubkey of the signing key
-genrule {
-    name: "microdroid_bootloader_pubkey_replaced",
-    tools: ["replace_bytes"],
-    srcs: [
-        ":microdroid_crosvm_bootloader", // input (bootloader)
-        ":microdroid_crosvm_bootloader.avbpubkey", // old bytes (old pubkey)
-        ":microdroid_bootloader_avbpubkey_gen", // new bytes (new pubkey)
-    ],
-    out: ["bootloader-pubkey-replaced"],
-    // 1. Copy the input to the output (replace_bytes modifies the file in-place)
-    // 2. Replace embedded pubkey with new one.
-    cmd: "cp $(location :microdroid_crosvm_bootloader) $(out) && " +
-        "$(location replace_bytes) $(out) " +
-        "$(location :microdroid_crosvm_bootloader.avbpubkey) " +
-        "$(location :microdroid_bootloader_avbpubkey_gen)",
-}
-
-// Apex keeps a copy of avbpubkey embedded in bootloader so that embedded avbpubkey can be replaced
-// while re-signing bootloader.
-prebuilt_etc {
-    name: "microdroid_bootloader.avbpubkey",
-    src: ":microdroid_bootloader_avbpubkey_gen",
-}
-
-// Generate avbpukey from the signing key
-genrule {
-    name: "microdroid_bootloader_avbpubkey_gen",
-    tools: ["avbtool"],
-    srcs: [":microdroid_sign_key"],
-    out: ["bootloader.pubkey"],
-    cmd: "$(location avbtool) extract_public_key " +
-        "--key $(location :microdroid_sign_key) " +
-        "--output $(out)",
-}
-
-// python -c "import hashlib; print(hashlib.sha256(b'uboot_env').hexdigest())"
-uboot_env_salt = "cbf2d76827ece5ca8d176a40c94ac6355edcf6511b4b887364a8c0e05850df10"
-
-avb_add_hash_footer {
-    name: "microdroid_uboot_env",
-    src: ":microdroid_uboot_env_gen",
-    filename: "uboot_env.img",
-    partition_name: "uboot_env",
-    private_key: ":microdroid_sign_key",
-    salt: uboot_env_salt,
-}
-
-genrule {
-    name: "microdroid_uboot_env_gen",
-    tools: ["mkenvimage_slim"],
-    srcs: ["uboot-env.txt"],
-    out: ["output.img"],
-    cmd: "$(location mkenvimage_slim) -output_path $(out) -input_path $(location uboot-env.txt)",
-}
-
 // Note that keys can be different for filesystem images even though we're using the same key
 // for microdroid. However, the key signing VBmeta should match with the pubkey embedded in
 // bootloader.
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
index 7a95ce6..ff6314b 100644
--- a/microdroid/initrd/Android.bp
+++ b/microdroid/initrd/Android.bp
@@ -68,7 +68,7 @@
         ":microdroid_bootconfig_debuggable_src",
     ] + bootconfigs_arm64,
     out: ["microdroid_initrd_debuggable_arm64"],
-    cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+    cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
 genrule {
@@ -79,7 +79,7 @@
         ":microdroid_bootconfig_debuggable_src",
     ] + bootconfigs_x86_64,
     out: ["microdroid_initrd_debuggable_x86_64"],
-    cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+    cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
 genrule {
@@ -90,7 +90,7 @@
         ":microdroid_bootconfig_normal_src",
     ] + bootconfigs_arm64,
     out: ["microdroid_initrd_normal_arm64"],
-    cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+    cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
 genrule {
@@ -101,7 +101,7 @@
         ":microdroid_bootconfig_normal_src",
     ] + bootconfigs_x86_64,
     out: ["microdroid_initrd_normal_x86_64"],
-    cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
+    cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
 prebuilt_etc {
diff --git a/microdroid/initrd/src/main.rs b/microdroid/initrd/src/main.rs
index 8c88000..3b0a7d2 100644
--- a/microdroid/initrd/src/main.rs
+++ b/microdroid/initrd/src/main.rs
@@ -12,31 +12,90 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Append bootconfig to initrd image
-use anyhow::Result;
+//! Attach/Detach bootconfigs to initrd image
+use anyhow::{bail, Result};
 use clap::Parser;
+use std::cmp::min;
 use std::fs::File;
-use std::io::{Read, Write};
+use std::io::{Read, Seek, SeekFrom, Write};
+use std::mem::size_of;
 use std::path::PathBuf;
 
 const FOOTER_ALIGNMENT: usize = 4;
 const ZEROS: [u8; 4] = [0u8; 4_usize];
+const BOOTCONFIG_MAGIC: &str = "#BOOTCONFIG\n";
+// Footer includes [size(le32)][checksum(le32)][#BOOTCONFIG\n] at the end of bootconfigs.
+const INITRD_FOOTER_LEN: usize = 2 * std::mem::size_of::<u32>() + BOOTCONFIG_MAGIC.len();
 
 #[derive(Parser, Debug)]
-struct Args {
-    /// Initrd (without bootconfig)
-    initrd: PathBuf,
-    /// Bootconfig
-    bootconfigs: Vec<PathBuf>,
-    /// Output
-    #[clap(long = "output")]
-    output: PathBuf,
+enum Opt {
+    /// Append bootconfig(s) to initrd image
+    Attach {
+        /// Initrd (without bootconfigs) <- Input
+        initrd: PathBuf,
+        /// Bootconfigs <- Input
+        bootconfigs: Vec<PathBuf>,
+        /// Initrd (with bootconfigs) <- Output
+        #[clap(long = "output")]
+        output: PathBuf,
+    },
+
+    /// Detach the initrd & bootconfigs - this is required for cases when we update
+    /// bootconfigs in sign_virt_apex
+    Detach {
+        /// Initrd (with bootconfigs) <- Input
+        initrd_with_bootconfigs: PathBuf,
+        /// Initrd (without bootconfigs) <- Output
+        initrd: PathBuf,
+        /// Bootconfigs <- Output
+        bootconfigs: PathBuf,
+    },
 }
 
 fn get_checksum(file_path: &PathBuf) -> Result<u32> {
     File::open(file_path)?.bytes().map(|x| Ok(x? as u32)).sum()
 }
 
+// Copy n bytes of file_in to file_out. Note: copying starts from the current cursors of files.
+// On successful return, the files' cursors would have moved forward by k bytes.
+fn copyfile2file(file_in: &mut File, file_out: &mut File, n: usize) -> Result<()> {
+    let mut buf = vec![0; 1024];
+    let mut copied: usize = 0;
+    while copied < n {
+        let k = min(n - copied, buf.len());
+        file_in.read_exact(&mut buf[..k])?;
+        file_out.write_all(&buf[..k])?;
+        copied += k;
+    }
+    Ok(())
+}
+
+// Note: attaching & then detaching bootconfigs can lead to extra padding in bootconfigs
+fn detach_bootconfig(initrd_bc: PathBuf, initrd: PathBuf, bootconfig: PathBuf) -> Result<()> {
+    let mut initrd_bc = File::open(&initrd_bc)?;
+    let mut bootconfig = File::create(&bootconfig)?;
+    let mut initrd = File::create(&initrd)?;
+    let initrd_bc_size: usize = initrd_bc.metadata()?.len().try_into()?;
+
+    initrd_bc.seek(SeekFrom::End(-(BOOTCONFIG_MAGIC.len() as i64)))?;
+    let mut magic_buf = [0; BOOTCONFIG_MAGIC.len()];
+    initrd_bc.read_exact(&mut magic_buf)?;
+    if magic_buf != BOOTCONFIG_MAGIC.as_bytes() {
+        bail!("BOOTCONFIG_MAGIC not found in initrd. Bootconfigs might not be attached correctly");
+    }
+    let mut size_buf = [0; size_of::<u32>()];
+    initrd_bc.seek(SeekFrom::End(-(INITRD_FOOTER_LEN as i64)))?;
+    initrd_bc.read_exact(&mut size_buf)?;
+    let bc_size: usize = u32::from_le_bytes(size_buf) as usize;
+
+    let initrd_size: usize = initrd_bc_size - bc_size - INITRD_FOOTER_LEN;
+
+    initrd_bc.seek(SeekFrom::Start(0))?;
+    copyfile2file(&mut initrd_bc, &mut initrd, initrd_size)?;
+    copyfile2file(&mut initrd_bc, &mut bootconfig, bc_size)?;
+    Ok(())
+}
+
 // Bootconfig is attached to the initrd in the following way:
 // [initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]
 fn attach_bootconfig(initrd: PathBuf, bootconfigs: Vec<PathBuf>, output: PathBuf) -> Result<()> {
@@ -59,14 +118,21 @@
     output_file.write_all(&ZEROS[..padding_size])?;
     output_file.write_all(&((padding_size + bootconfig_size) as u32).to_le_bytes())?;
     output_file.write_all(&checksum.to_le_bytes())?;
-    output_file.write_all(b"#BOOTCONFIG\n")?;
+    output_file.write_all(BOOTCONFIG_MAGIC.as_bytes())?;
     output_file.flush()?;
     Ok(())
 }
 
 fn try_main() -> Result<()> {
-    let args = Args::parse();
-    attach_bootconfig(args.initrd, args.bootconfigs, args.output)?;
+    let args = Opt::parse();
+    match args {
+        Opt::Attach { initrd, bootconfigs, output } => {
+            attach_bootconfig(initrd, bootconfigs, output)?
+        }
+        Opt::Detach { initrd_with_bootconfigs, initrd, bootconfigs } => {
+            detach_bootconfig(initrd_with_bootconfigs, initrd, bootconfigs)?
+        }
+    };
     Ok(())
 }
 
@@ -82,6 +148,6 @@
     #[test]
     fn verify_args() {
         // Check that the command parsing has been configured in a valid way.
-        Args::command().debug_assert();
+        Opt::command().debug_assert();
     }
 }
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 7679c57..6e0cf5a 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -29,6 +29,7 @@
         // For re-sign test
         "avbtool",
         "img2simg",
+        "initrd_bootconfig",
         "lpmake",
         "lpunpack",
         "mk_payload",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 87129bb..112041b 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -55,7 +55,6 @@
 import org.json.JSONObject;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestName;
@@ -173,7 +172,11 @@
                 .isSuccess();
     }
 
-    private void resignVirtApex(File virtApexDir, File signingKey, Map<String, File> keyOverrides) {
+    private void resignVirtApex(
+            File virtApexDir,
+            File signingKey,
+            Map<String, File> keyOverrides,
+            boolean updateBootconfigs) {
         File signVirtApex = findTestFile("sign_virt_apex");
 
         RunUtil runUtil = new RunUtil();
@@ -184,6 +187,9 @@
 
         List<String> command = new ArrayList<>();
         command.add(signVirtApex.getAbsolutePath());
+        if (!updateBootconfigs) {
+            command.add("--do_not_update_bootconfigs");
+        }
         keyOverrides.forEach(
                 (filename, keyFile) ->
                         command.add("--key_override " + filename + "=" + keyFile.getPath()));
@@ -271,7 +277,11 @@
     }
 
     private VmInfo runMicrodroidWithResignedImages(
-            File key, Map<String, File> keyOverrides, boolean isProtected) throws Exception {
+            File key,
+            Map<String, File> keyOverrides,
+            boolean isProtected,
+            boolean updateBootconfigs)
+            throws Exception {
         CommandRunner android = new CommandRunner(getDevice());
 
         File virtApexDir = FileUtil.createTempDir("virt_apex");
@@ -284,7 +294,7 @@
         assertWithMessage("Failed to pull " + VIRT_APEX + "etc")
                 .that(getDevice().pullDir(VIRT_APEX + "etc", virtApexEtcDir)).isTrue();
 
-        resignVirtApex(virtApexDir, key, keyOverrides);
+        resignVirtApex(virtApexDir, key, keyOverrides, updateBootconfigs);
 
         // Push back re-signed virt APEX contents and updated microdroid.json
         getDevice().pushDir(virtApexDir, TEST_ROOT);
@@ -460,7 +470,8 @@
 
         // Act
         VmInfo vmInfo =
-                runMicrodroidWithResignedImages(key, /*keyOverrides=*/ Map.of(), protectedVm);
+                runMicrodroidWithResignedImages(
+                        key, /*keyOverrides=*/ Map.of(), protectedVm, /*updateBootconfigs=*/ true);
 
         // Assert
         vmInfo.mProcess.waitFor(5L, TimeUnit.SECONDS);
@@ -471,16 +482,15 @@
         vmInfo.mProcess.destroy();
     }
 
-    // TODO(b/245277660): Resigning the system/vendor image changes the vbmeta hash.
-    // So, unless vbmeta related bootconfigs are updated the following test will fail
     @Test
-    @Ignore("b/245277660")
     @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
     public void testBootSucceedsWhenNonProtectedVmStartsWithImagesSignedWithDifferentKey()
             throws Exception {
         File key = findTestFile("test.com.android.virt.pem");
         Map<String, File> keyOverrides = Map.of();
-        VmInfo vmInfo = runMicrodroidWithResignedImages(key, keyOverrides, /*isProtected=*/ false);
+        VmInfo vmInfo =
+                runMicrodroidWithResignedImages(
+                        key, keyOverrides, /*isProtected=*/ false, /*updateBootconfigs=*/ true);
         // Device online means that boot must have succeeded.
         adbConnectToMicrodroid(getDevice(), vmInfo.mCid);
         vmInfo.mProcess.destroy();
@@ -491,10 +501,10 @@
     public void testBootFailsWhenVbMetaDigestDoesNotMatchBootconfig() throws Exception {
         // Sign everything with key1 except vbmeta
         File key = findTestFile("test.com.android.virt.pem");
-        File key2 = findTestFile("test2.com.android.virt.pem");
-        Map<String, File> keyOverrides = Map.of("microdroid_vbmeta.img", key2);
         // To be able to stop it, it should be a daemon.
-        VmInfo vmInfo = runMicrodroidWithResignedImages(key, keyOverrides, /*isProtected=*/ false);
+        VmInfo vmInfo =
+                runMicrodroidWithResignedImages(
+                        key, Map.of(), /*isProtected=*/ false, /*updateBootconfigs=*/ false);
         // Wait so that init can print errors to console (time in cuttlefish >> in real device)
         assertThatEventually(
                 100000,
@@ -839,6 +849,64 @@
         assertThat(ret).contains("Payload binary name must not specify a path");
     }
 
+    @Test
+    @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
+    public void testAllVbmetaUseSHA256() throws Exception {
+        File virtApexDir = FileUtil.createTempDir("virt_apex");
+        // Pull the virt apex's etc/ directory (which contains images)
+        File virtApexEtcDir = new File(virtApexDir, "etc");
+        // We need only etc/ directory for images
+        assertWithMessage("Failed to mkdir " + virtApexEtcDir)
+                .that(virtApexEtcDir.mkdirs())
+                .isTrue();
+        assertWithMessage("Failed to pull " + VIRT_APEX + "etc")
+                .that(getDevice().pullDir(VIRT_APEX + "etc", virtApexEtcDir))
+                .isTrue();
+
+        checkHashAlgorithm(virtApexEtcDir);
+    }
+
+    private String avbInfo(String image_path) throws Exception {
+        File avbtool = findTestFile("avbtool");
+        List<String> command =
+                Arrays.asList(avbtool.getAbsolutePath(), "info_image", "--image", image_path);
+        CommandResult result =
+                new RunUtil().runTimedCmd(5000, "/bin/bash", "-c", String.join(" ", command));
+        String out = result.getStdout();
+        String err = result.getStderr();
+        assertWithMessage(
+                        "Command "
+                                + command
+                                + " failed."
+                                + ":\n\tout: "
+                                + out
+                                + "\n\terr: "
+                                + err
+                                + "\n")
+                .about(command_results())
+                .that(result)
+                .isSuccess();
+        return out;
+    }
+
+    private void checkHashAlgorithm(File virtApexEtcDir) throws Exception {
+        List<String> images =
+                Arrays.asList(
+                        // kernel image (contains descriptors from initrd(s) as well)
+                        "/fs/microdroid_kernel",
+                        // vbmeta partition (contains descriptors from vendor/system images)
+                        "/fs/microdroid_vbmeta.img");
+
+        for (String path : images) {
+            String info = avbInfo(virtApexEtcDir + path);
+            Pattern pattern = Pattern.compile("Hash Algorithm:[ ]*(sha1|sha256)");
+            Matcher m = pattern.matcher(info);
+            while (m.find()) {
+                assertThat(m.group(1)).isEqualTo("sha256");
+            }
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         testIfDeviceIsCapable(getDevice());
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 4773c0b..8f88daf 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -78,9 +78,9 @@
     /// triggered.
     static ref BOOT_HANGUP_TIMEOUT: Duration = if nested_virt::is_nested_virtualization().unwrap() {
         // Nested virtualization is slow, so we need a longer timeout.
-        Duration::from_secs(100)
+        Duration::from_secs(300)
     } else {
-        Duration::from_secs(10)
+        Duration::from_secs(30)
     };
 }