Merge "Revert "Upgrade zerocopy to 0.7.5"" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index f5d2dda..f1bfe09 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -31,6 +31,9 @@
     },
     {
       "name": "libdice_policy.test"
+    },
+    {
+      "name": "libapkzip.test"
     }
   ],
   "avf-postsubmit": [
diff --git a/javalib/api/current.txt b/javalib/api/current.txt
index 14191eb..d802177 100644
--- a/javalib/api/current.txt
+++ b/javalib/api/current.txt
@@ -1,3 +1 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/javalib/api/module-lib-current.txt b/javalib/api/module-lib-current.txt
index 16a5131..4d59764 100644
--- a/javalib/api/module-lib-current.txt
+++ b/javalib/api/module-lib-current.txt
@@ -1,6 +1,4 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
 package android.system.virtualmachine {
 
   public class VirtualizationFrameworkInitializer {
diff --git a/javalib/api/module-lib-removed.txt b/javalib/api/module-lib-removed.txt
index 14191eb..d802177 100644
--- a/javalib/api/module-lib-removed.txt
+++ b/javalib/api/module-lib-removed.txt
@@ -1,3 +1 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/javalib/api/removed.txt b/javalib/api/removed.txt
index 14191eb..d802177 100644
--- a/javalib/api/removed.txt
+++ b/javalib/api/removed.txt
@@ -1,3 +1 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index 840c6fa..d9bafa1 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -1,6 +1,4 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
 package android.system.virtualmachine {
 
   public class VirtualMachine implements java.lang.AutoCloseable {
diff --git a/javalib/api/system-removed.txt b/javalib/api/system-removed.txt
index 14191eb..d802177 100644
--- a/javalib/api/system-removed.txt
+++ b/javalib/api/system-removed.txt
@@ -1,3 +1 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/javalib/api/test-current.txt b/javalib/api/test-current.txt
index e8bb684..12c099d 100644
--- a/javalib/api/test-current.txt
+++ b/javalib/api/test-current.txt
@@ -1,6 +1,4 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
 package android.system.virtualmachine {
 
   public class VirtualMachine implements java.lang.AutoCloseable {
diff --git a/javalib/api/test-removed.txt b/javalib/api/test-removed.txt
index 14191eb..d802177 100644
--- a/javalib/api/test-removed.txt
+++ b/javalib/api/test-removed.txt
@@ -1,3 +1 @@
 // Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/libs/apkmanifest/Android.bp b/libs/apkmanifest/Android.bp
new file mode 100644
index 0000000..e6fcbef
--- /dev/null
+++ b/libs/apkmanifest/Android.bp
@@ -0,0 +1,46 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_shared {
+    name: "libapkmanifest_native",
+    srcs: ["native/*.cpp"],
+    shared_libs: [
+        "libandroidfw",
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+}
+
+rust_bindgen {
+    name: "libapkmanifest_bindgen",
+    defaults: ["avf_build_flags_rust"],
+    edition: "2021",
+    wrapper_src: "native/apkmanifest.hpp",
+    crate_name: "apkmanifest_bindgen",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--default-enum-style rust",
+    ],
+}
+
+rust_library {
+    name: "libapkmanifest",
+    crate_name: "apkmanifest",
+    defaults: ["avf_build_flags_rust"],
+    edition: "2021",
+    srcs: ["src/apkmanifest.rs"],
+    rustlibs: [
+        "libanyhow",
+        "libapkzip",
+        "libapkmanifest_bindgen",
+        "libscopeguard",
+    ],
+    shared_libs: ["libapkmanifest_native"],
+    multilib: {
+        lib32: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libs/apkmanifest/native/apkmanifest.cpp b/libs/apkmanifest/native/apkmanifest.cpp
new file mode 100644
index 0000000..ab0ba72
--- /dev/null
+++ b/libs/apkmanifest/native/apkmanifest.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apkmanifest.hpp"
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+#include <androidfw/AssetsProvider.h>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/StringPiece.h>
+#include <androidfw/Util.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <utils/Errors.h>
+
+#include <cstdlib>
+#include <limits>
+#include <string>
+#include <string_view>
+
+using android::Asset;
+using android::AssetsProvider;
+using android::OK;
+using android::Res_value;
+using android::ResXMLParser;
+using android::ResXMLTree;
+using android::statusToString;
+using android::StringPiece16;
+using android::base::Error;
+using android::base::Result;
+using android::util::Utf16ToUtf8;
+using std::u16string_view;
+using std::unique_ptr;
+
+struct ApkManifestInfo {
+    std::string package;
+    uint32_t version_code;
+    uint32_t version_code_major;
+};
+
+namespace {
+// See https://developer.android.com/guide/topics/manifest/manifest-element
+constexpr u16string_view MANIFEST_TAG_NAME{u"manifest"};
+constexpr u16string_view ANDROID_NAMESPACE_URL{u"http://schemas.android.com/apk/res/android"};
+constexpr u16string_view PACKAGE_ATTRIBUTE_NAME{u"package"};
+constexpr u16string_view VERSION_CODE_ATTRIBUTE_NAME{u"versionCode"};
+constexpr u16string_view VERSION_CODE_MAJOR_ATTRIBUTE_NAME{u"versionCodeMajor"};
+
+// Read through the XML parse tree up to the <manifest> element.
+Result<void> findManifestElement(ResXMLTree& tree) {
+    for (;;) {
+        ResXMLParser::event_code_t event = tree.next();
+        switch (event) {
+            case ResXMLParser::END_DOCUMENT:
+            case ResXMLParser::END_TAG:
+            case ResXMLParser::TEXT:
+            default:
+                return Error() << "Unexpected XML parsing event: " << event;
+            case ResXMLParser::BAD_DOCUMENT:
+                return Error() << "Failed to parse XML: " << statusToString(tree.getError());
+            case ResXMLParser::START_NAMESPACE:
+            case ResXMLParser::END_NAMESPACE:
+                // Not of interest, keep going.
+                break;
+            case ResXMLParser::START_TAG:
+                // The first tag in an AndroidManifest.xml should be <manifest> (no namespace).
+                // And that's actually the only tag we care about.
+                if (tree.getElementNamespaceID() >= 0) {
+                    return Error() << "Root element has unexpected namespace.";
+                }
+                size_t nameLength = 0;
+                const char16_t* nameChars = tree.getElementName(&nameLength);
+                if (!nameChars) {
+                    return Error() << "Missing tag name";
+                }
+                if (u16string_view(nameChars, nameLength) != MANIFEST_TAG_NAME) {
+                    return Error() << "Expected <manifest> as root element";
+                }
+                return {};
+        }
+    }
+}
+
+// Return an attribute encoded as a string, converted to UTF-8. Note that all
+// attributes are strings in the original XML, but the binary format encodes
+// some as binary numbers etc. This function does not handle converting those
+// encodings back to strings, so should only be used when it is known that a
+// numeric value is not allowed.
+Result<std::string> getStringOnlyAttribute(const ResXMLTree& tree, size_t index) {
+    size_t len;
+    const char16_t* value = tree.getAttributeStringValue(index, &len);
+    if (!value) {
+        return Error() << "Expected attribute to have string value";
+    }
+    return Utf16ToUtf8(StringPiece16(value, len));
+}
+
+// Return the u32 value of an attribute.
+Result<uint32_t> getU32Attribute(const ResXMLTree& tree, size_t index) {
+    auto type = tree.getAttributeDataType(index);
+    switch (type) {
+        case Res_value::TYPE_INT_DEC:
+        case Res_value::TYPE_INT_HEX:
+            // This is how we'd expect the version to be encoded - and we don't
+            // care what base it was originally in.
+            return tree.getAttributeData(index);
+        case Res_value::TYPE_STRING: {
+            // If the original string is encoded, then we need to convert it.
+            auto str = OR_RETURN(getStringOnlyAttribute(tree, index));
+            char* str_end = nullptr;
+            // Note that by specifying base 0 we allow for octal, hex, or
+            // decimal representations here.
+            unsigned long value = std::strtoul(str.c_str(), &str_end, 0);
+            if (str_end != str.c_str() + str.size() ||
+                value > std::numeric_limits<uint32_t>::max()) {
+                return Error() << "Invalid numeric value";
+            }
+            return static_cast<uint32_t>(value);
+        }
+        default:
+            return Error() << "Expected numeric value, got type " << type;
+    }
+}
+
+// Parse the binary manifest and extract the information we care about.
+// Everything we're interested in should be an attribute on the <manifest> tag.
+// We don't care what order they come in, absent attributes will be treated as
+// the default value, and any unknown attributes (including ones not in the
+// expected namespace) will be ignored.
+Result<unique_ptr<ApkManifestInfo>> parseManifest(const void* manifest, size_t size) {
+    ResXMLTree tree;
+    auto status = tree.setTo(manifest, size);
+    if (status != OK) {
+        return Error() << "Failed to create XML Tree: " << statusToString(status);
+    }
+
+    OR_RETURN(findManifestElement(tree));
+
+    unique_ptr<ApkManifestInfo> info{new ApkManifestInfo{}};
+
+    size_t count = tree.getAttributeCount();
+    for (size_t i = 0; i < count; ++i) {
+        size_t len;
+        const char16_t* chars;
+
+        chars = tree.getAttributeNamespace(i, &len);
+        auto namespaceUrl = chars ? u16string_view(chars, len) : u16string_view();
+
+        chars = tree.getAttributeName(i, &len);
+        auto attributeName = chars ? u16string_view(chars, len) : u16string_view();
+
+        if (namespaceUrl.empty()) {
+            if (attributeName == PACKAGE_ATTRIBUTE_NAME) {
+                auto result = getStringOnlyAttribute(tree, i);
+                if (!result.ok()) return Error() << "Package name: " << result.error();
+                info->package = *result;
+            }
+        } else if (namespaceUrl == ANDROID_NAMESPACE_URL) {
+            if (attributeName == VERSION_CODE_ATTRIBUTE_NAME) {
+                auto result = getU32Attribute(tree, i);
+                if (!result.ok()) return Error() << "Version code: " << result.error();
+                info->version_code = *result;
+            } else if (attributeName == VERSION_CODE_MAJOR_ATTRIBUTE_NAME) {
+                auto result = getU32Attribute(tree, i);
+                if (!result.ok()) return Error() << "Version code major: " << result.error();
+                info->version_code_major = *result;
+            }
+        }
+    }
+
+    return info;
+}
+} // namespace
+
+const ApkManifestInfo* extractManifestInfo(const void* manifest, size_t size) {
+    auto result = parseManifest(manifest, size);
+    if (!result.ok()) {
+        LOG(ERROR) << "Failed to parse APK manifest:" << result.error().message();
+        return nullptr;
+    }
+    return result->release();
+}
+
+void freeManifestInfo(const ApkManifestInfo* info) {
+    delete info;
+}
+
+const char* getPackageName(const ApkManifestInfo* info) {
+    return info->package.c_str();
+}
+
+uint64_t getVersionCode(const ApkManifestInfo* info) {
+    return info->version_code | (static_cast<uint64_t>(info->version_code_major) << 32);
+}
diff --git a/libs/apkmanifest/native/apkmanifest.hpp b/libs/apkmanifest/native/apkmanifest.hpp
new file mode 100644
index 0000000..352912e
--- /dev/null
+++ b/libs/apkmanifest/native/apkmanifest.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+// Opaque structure holding information extracted from an APK manifest.
+struct ApkManifestInfo;
+
+extern "C" {
+
+// Parse a binary XML encoded APK manifest and extract relevant information.
+// The caller must free the returned pointer using freeManifestInfo.  Returns
+// null if any error occurs. Does not retain any pointer to the manifest
+// provided.
+const ApkManifestInfo* extractManifestInfo(const void* manifest, size_t size);
+
+// Frees an ApkManifestInfo allocated by extractManifestInfo; this invalidates
+// the pointer and it must not be used again.
+void freeManifestInfo(const ApkManifestInfo* info);
+
+// Given a valid ApkManifestInfo pointer, return the package name of the APK, as
+// a nul-terminated UTF-8 string. The pointer remains valid until the
+// ApkManifestInfo is freed.
+const char* getPackageName(const ApkManifestInfo* info);
+
+// Given a valid ApkManifestInfo pointer, return the version code of the APK.
+uint64_t getVersionCode(const ApkManifestInfo* info);
+}
diff --git a/libs/apkmanifest/src/apkmanifest.rs b/libs/apkmanifest/src/apkmanifest.rs
new file mode 100644
index 0000000..6766b21
--- /dev/null
+++ b/libs/apkmanifest/src/apkmanifest.rs
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Handle parsing of APK manifest files.
+//! The manifest file is written as XML text, but is stored in the APK
+//! as Android binary compressed XML. This library is a wrapper around
+//! a thin C++ wrapper around libandroidfw, which contains the same
+//! parsing code as used by package manager and aapt2 (amongst other
+//! things).
+
+use anyhow::{bail, Context, Result};
+use apkmanifest_bindgen::{extractManifestInfo, freeManifestInfo, getPackageName, getVersionCode};
+use std::ffi::CStr;
+use std::fs::File;
+use std::path::Path;
+
+/// Information extracted from the Android manifest inside an APK.
+#[derive(Debug)]
+pub struct ApkManifestInfo {
+    /// The package name of the app.
+    pub package: String,
+    /// The version code of the app.
+    pub version_code: u64,
+}
+
+const ANDROID_MANIFEST: &str = "AndroidManifest.xml";
+
+/// Find the manifest inside the given APK and return information from it.
+pub fn get_manifest_info<P: AsRef<Path>>(apk_path: P) -> Result<ApkManifestInfo> {
+    let apk = File::open(apk_path.as_ref())?;
+    let manifest = apkzip::read_file(apk, ANDROID_MANIFEST)?;
+
+    // Safety: The function only reads the memory range we specify and does not hold
+    // any reference to it.
+    let native_info = unsafe { extractManifestInfo(manifest.as_ptr() as _, manifest.len()) };
+    if native_info.is_null() {
+        bail!("Failed to parse manifest")
+    };
+
+    scopeguard::defer! {
+        // Safety: The value we pass is the result of calling extractManifestInfo as required.
+        // We must call this exactly once, after we have finished using it, which the scopeguard
+        // ensures.
+        unsafe { freeManifestInfo(native_info); }
+    }
+
+    // Safety: It is always safe to call this with a valid native_info, which we have,
+    // and it always returns a valid nul-terminated C string with the same lifetime as native_info.
+    // We immediately make a copy.
+    let package = unsafe { CStr::from_ptr(getPackageName(native_info)) };
+    let package = package.to_str().context("Invalid package name")?.to_string();
+
+    // Safety: It is always safe to call this with a valid native_info, which we have.
+    let version_code = unsafe { getVersionCode(native_info) };
+
+    Ok(ApkManifestInfo { package, version_code })
+}
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index d3aa7ee..1c18d2d 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -7,10 +7,10 @@
     crate_name: "apkverify",
     defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
-    prefer_rlib: true,
     edition: "2021",
     rustlibs: [
         "libanyhow",
+        "libapkzip",
         "libbyteorder",
         "libbytes",
         "libhex",
@@ -18,7 +18,6 @@
         "libnum_traits",
         "libopenssl",
         "libserde",
-        "libzip",
     ],
     proc_macros: ["libnum_derive"],
 }
@@ -34,6 +33,7 @@
 rust_test {
     name: "libapkverify.test",
     defaults: ["libapkverify.defaults"],
+    prefer_rlib: true,
     test_suites: ["general-tests"],
     data: ["tests/data/*"],
 }
@@ -49,6 +49,8 @@
     rustlibs: [
         "libandroid_logger",
         "libapkverify",
+        "libapkzip",
+        "libbyteorder",
         "liblog_rust",
         "libzip",
     ],
diff --git a/libs/apkverify/src/lib.rs b/libs/apkverify/src/lib.rs
index f7cbb7e..6af8122 100644
--- a/libs/apkverify/src/lib.rs
+++ b/libs/apkverify/src/lib.rs
@@ -24,7 +24,6 @@
 pub mod testing;
 mod v3;
 mod v4;
-mod ziputil;
 
 pub use algorithms::{HashAlgorithm, SignatureAlgorithmID};
 pub use v3::{get_public_key_der, verify};
diff --git a/libs/apkverify/src/sigutil.rs b/libs/apkverify/src/sigutil.rs
index 395b493..7d03bb2 100644
--- a/libs/apkverify/src/sigutil.rs
+++ b/libs/apkverify/src/sigutil.rs
@@ -17,6 +17,7 @@
 //! Utilities for Signature Verification
 
 use anyhow::{anyhow, ensure, Error, Result};
+use apkzip::{set_central_directory_offset, zip_sections};
 use byteorder::{LittleEndian, ReadBytesExt};
 use bytes::{Buf, BufMut, Bytes, BytesMut};
 use openssl::hash::{DigestBytes, Hasher, MessageDigest};
@@ -24,7 +25,6 @@
 use std::io::{self, Cursor, ErrorKind, Read, Seek, SeekFrom, Take};
 
 use crate::algorithms::SignatureAlgorithmID;
-use crate::ziputil::{set_central_directory_offset, zip_sections};
 
 const APK_SIG_BLOCK_MIN_SIZE: u32 = 32;
 const APK_SIG_BLOCK_MAGIC: u128 = 0x3234206b636f6c4220676953204b5041;
@@ -51,8 +51,8 @@
 }
 
 impl<R: Read + Seek> ApkSections<R> {
-    pub fn new(reader: R) -> Result<ApkSections<R>> {
-        let (mut reader, zip_sections) = zip_sections(reader)?;
+    pub fn new(mut reader: R) -> Result<ApkSections<R>> {
+        let zip_sections = zip_sections(&mut reader)?;
         let (signing_block_offset, signing_block_size) =
             find_signing_block(&mut reader, zip_sections.central_directory_offset)?;
         Ok(ApkSections {
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 6082422..8a8ad73 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -29,7 +29,7 @@
 
 use crate::algorithms::SignatureAlgorithmID;
 use crate::bytes_ext::{BytesExt, LengthPrefixed, ReadFromBytes};
-use crate::sigutil::*;
+use crate::sigutil::ApkSections;
 
 pub const APK_SIGNATURE_SCHEME_V3_BLOCK_ID: u32 = 0xf05368c0;
 
@@ -161,7 +161,8 @@
         // 1. Choose the strongest supported signature algorithm ID from signatures.
         let strongest = self.strongest_signature()?;
 
-        // 2. Verify the corresponding signature from signatures against signed data using public key.
+        // 2. Verify the corresponding signature from signatures against signed data using public
+        // key.
         let verified_signed_data = self.verify_signature(strongest)?;
 
         // 3. Verify the min and max SDK versions in the signed data match those specified for the
@@ -196,8 +197,8 @@
             hex::encode(digest.digest.as_ref()),
         );
 
-        // 7. Verify that public key of the first certificate of certificates is identical
-        //    to public key.
+        // 7. Verify that public key of the first certificate of certificates is identical to public
+        //    key.
         let cert = verified_signed_data.certificates.first().context("No certificates listed")?;
         let cert = X509::from_der(cert.as_ref())?;
         ensure!(
diff --git a/libs/apkverify/tests/apkverify_test.rs b/libs/apkverify/tests/apkverify_test.rs
index 52e1da4..680c81e 100644
--- a/libs/apkverify/tests/apkverify_test.rs
+++ b/libs/apkverify/tests/apkverify_test.rs
@@ -17,7 +17,10 @@
 use apkverify::{
     get_apk_digest, get_public_key_der, testing::assert_contains, verify, SignatureAlgorithmID,
 };
+use apkzip::zip_sections;
+use byteorder::{LittleEndian, ReadBytesExt};
 use log::info;
+use std::io::{Seek, SeekFrom};
 use std::{fs, matches, path::Path};
 
 const KEY_NAMES_DSA: &[&str] = &["1024", "2048", "3072"];
@@ -37,6 +40,28 @@
 }
 
 #[test]
+fn test_zip_sections_with_apk() {
+    let mut reader = fs::File::open("tests/data/v3-only-with-stamp.apk").unwrap();
+    let sections = zip_sections(&mut reader).unwrap();
+
+    // Checks Central directory.
+    assert_eq!(
+        sections.central_directory_offset + sections.central_directory_size,
+        sections.eocd_offset
+    );
+
+    // Checks EOCD.
+    const EOCD_SIGNATURE: u32 = 0x06054b50;
+
+    reader.seek(SeekFrom::Start(sections.eocd_offset as u64)).unwrap();
+    assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), EOCD_SIGNATURE);
+    assert_eq!(
+        reader.metadata().unwrap().len(),
+        (sections.eocd_offset + sections.eocd_size) as u64
+    );
+}
+
+#[test]
 fn test_verify_truncated_cd() {
     setup();
     use zip::result::ZipError;
@@ -284,7 +309,7 @@
     let apk = fs::File::open(&apk_path).expect("Unabled to open apk file");
 
     let (verified_algorithm_id, verified_digest) =
-        get_apk_digest(&apk, SDK_INT, /*verify=*/ true)
+        get_apk_digest(&apk, SDK_INT, /* verify= */ true)
             .expect("Error when extracting apk digest with verification.");
 
     assert_eq!(expected_algorithm_id, verified_algorithm_id);
@@ -292,7 +317,7 @@
     assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
 
     let (unverified_algorithm_id, unverified_digest) =
-        get_apk_digest(&apk, SDK_INT, /*verify=*/ false)
+        get_apk_digest(&apk, SDK_INT, /* verify= */ false)
             .expect("Error when extracting apk digest without verification.");
     assert_eq!(expected_algorithm_id, unverified_algorithm_id);
     assert_eq!(verified_digest, unverified_digest);
diff --git a/libs/apkzip/Android.bp b/libs/apkzip/Android.bp
new file mode 100644
index 0000000..dc35b5e
--- /dev/null
+++ b/libs/apkzip/Android.bp
@@ -0,0 +1,35 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "libapkzip.defaults",
+    crate_name: "apkzip",
+    defaults: ["avf_build_flags_rust"],
+    edition: "2021",
+    srcs: ["src/ziputil.rs"],
+    rustlibs: [
+        "libanyhow",
+        "libbytes",
+        "liblog_rust",
+        "libanyhow",
+        "libzip",
+    ],
+}
+
+rust_library {
+    name: "libapkzip",
+    defaults: ["libapkzip.defaults"],
+    host_supported: true,
+    apex_available: ["com.android.virt"],
+}
+
+rust_test {
+    name: "libapkzip.test",
+    defaults: ["libapkzip.defaults"],
+    prefer_rlib: true,
+    test_suites: ["general-tests"],
+    rustlibs: [
+        "libbyteorder",
+    ],
+}
diff --git a/libs/apkverify/src/ziputil.rs b/libs/apkzip/src/ziputil.rs
similarity index 74%
rename from libs/apkverify/src/ziputil.rs
rename to libs/apkzip/src/ziputil.rs
index 5e513a7..708bbcc 100644
--- a/libs/apkverify/src/ziputil.rs
+++ b/libs/apkzip/src/ziputil.rs
@@ -21,9 +21,6 @@
 use std::io::{Read, Seek};
 use zip::ZipArchive;
 
-#[cfg(test)]
-use std::io::SeekFrom;
-
 const EOCD_SIZE_WITHOUT_COMMENT: usize = 22;
 const EOCD_CENTRAL_DIRECTORY_SIZE_FIELD_OFFSET: usize = 12;
 const EOCD_CENTRAL_DIRECTORY_OFFSET_FIELD_OFFSET: usize = 16;
@@ -31,16 +28,21 @@
 const EOCD_SIGNATURE: u32 = 0x06054b50;
 const ZIP64_MARK: u32 = 0xffffffff;
 
+/// Information about the layout of a zip file.
 #[derive(Debug, PartialEq, Eq)]
 pub struct ZipSections {
+    /// Offset within the file of the central directory.
     pub central_directory_offset: u32,
+    /// Size of the central directory.
     pub central_directory_size: u32,
+    /// Offset within the file of end of central directory marker.
     pub eocd_offset: u32,
+    /// Size of the end of central directory marker.
     pub eocd_size: u32,
 }
 
 /// Discover the layout of a zip file.
-pub fn zip_sections<R: Read + Seek>(mut reader: R) -> Result<(R, ZipSections)> {
+pub fn zip_sections<R: Read + Seek>(mut reader: R) -> Result<ZipSections> {
     // open a zip to parse EOCD
     let archive = ZipArchive::new(reader)?;
     let eocd_size = archive.comment().len() + EOCD_SIZE_WITHOUT_COMMENT;
@@ -65,15 +67,12 @@
         "Invalid ZIP: EOCD should follow CD with no extra data or overlap."
     );
 
-    Ok((
-        reader,
-        ZipSections {
-            central_directory_offset,
-            central_directory_size,
-            eocd_offset,
-            eocd_size: eocd_size as u32,
-        },
-    ))
+    Ok(ZipSections {
+        central_directory_offset,
+        central_directory_size,
+        eocd_offset,
+        eocd_size: eocd_size as u32,
+    })
 }
 
 fn get_central_directory(buf: &[u8]) -> Result<(u32, u32)> {
@@ -91,25 +90,39 @@
     Ok(())
 }
 
+/// Read an entire file from a .zip file into memory and return it.
+pub fn read_file<R: Read + Seek>(reader: R, file_name: &str) -> Result<Vec<u8>> {
+    let mut archive = ZipArchive::new(reader)?;
+    let mut file = archive.by_name(file_name)?;
+    let mut bytes = Vec::with_capacity(file.size() as usize);
+    file.read_to_end(&mut bytes)?;
+    Ok(bytes)
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::testing::assert_contains;
-    use byteorder::{LittleEndian, ReadBytesExt};
-    use std::fs::File;
     use std::io::{Cursor, Write};
     use zip::{write::FileOptions, ZipWriter};
 
+    const FILE_CONTENT: &[u8] = b"testcontent";
+    const FILE_NAME: &str = "testfile";
+
     fn create_test_zip() -> Cursor<Vec<u8>> {
         let mut writer = ZipWriter::new(Cursor::new(Vec::new()));
-        writer.start_file("testfile", FileOptions::default()).unwrap();
-        writer.write_all(b"testcontent").unwrap();
+        writer.start_file(FILE_NAME, FileOptions::default()).unwrap();
+        writer.write_all(FILE_CONTENT).unwrap();
         writer.finish().unwrap()
     }
 
+    fn assert_contains(haystack: &str, needle: &str) {
+        assert!(haystack.contains(needle), "{} is not found in {}", needle, haystack);
+    }
+
     #[test]
     fn test_zip_sections() {
-        let (cursor, sections) = zip_sections(create_test_zip()).unwrap();
+        let mut cursor = create_test_zip();
+        let sections = zip_sections(&mut cursor).unwrap();
         assert_eq!(
             sections.eocd_offset,
             (cursor.get_ref().len() - EOCD_SIZE_WITHOUT_COMMENT) as u32
@@ -117,6 +130,12 @@
     }
 
     #[test]
+    fn test_read_file() {
+        let file = read_file(create_test_zip(), FILE_NAME).unwrap();
+        assert_eq!(file.as_slice(), FILE_CONTENT);
+    }
+
+    #[test]
     fn test_reject_if_extra_data_between_cd_and_eocd() {
         // prepare normal zip
         let buf = create_test_zip().into_inner();
@@ -133,24 +152,4 @@
         assert!(res.is_err());
         assert_contains(&res.err().unwrap().to_string(), "Invalid ZIP: offset should be 0");
     }
-
-    #[test]
-    fn test_zip_sections_with_apk() {
-        let apk = File::open("tests/data/v3-only-with-stamp.apk").unwrap();
-        let (mut reader, sections) = zip_sections(apk).unwrap();
-
-        // Checks Central directory.
-        assert_eq!(
-            sections.central_directory_offset + sections.central_directory_size,
-            sections.eocd_offset
-        );
-
-        // Checks EOCD.
-        reader.seek(SeekFrom::Start(sections.eocd_offset as u64)).unwrap();
-        assert_eq!(reader.read_u32::<LittleEndian>().unwrap(), EOCD_SIGNATURE);
-        assert_eq!(
-            reader.metadata().unwrap().len(),
-            (sections.eocd_offset + sections.eocd_size) as u64
-        );
-    }
 }
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 758df2a..a6d5739 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -202,11 +202,11 @@
 }
 
 impl<'a> FdtNode<'a> {
-    /// Create immutable node from a mutable node at the same offset
+    /// Creates immutable node from a mutable node at the same offset.
     pub fn from_mut(other: &'a FdtNodeMut) -> Self {
         FdtNode { fdt: other.fdt, offset: other.offset }
     }
-    /// Find parent node.
+    /// Returns parent node.
     pub fn parent(&self) -> Result<Self> {
         // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
         let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
@@ -214,12 +214,12 @@
         Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
     }
 
-    /// Retrieve the standard (deprecated) device_type <string> property.
+    /// Returns the standard (deprecated) device_type <string> property.
     pub fn device_type(&self) -> Result<Option<&CStr>> {
         self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
     }
 
-    /// Retrieve the standard reg <prop-encoded-array> property.
+    /// Returns the standard reg <prop-encoded-array> property.
     pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
         let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
 
@@ -235,7 +235,7 @@
         }
     }
 
-    /// Retrieves the standard ranges property.
+    /// Returns the standard ranges property.
     pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
         let ranges = CStr::from_bytes_with_nul(b"ranges\0").unwrap();
         if let Some(cells) = self.getprop_cells(ranges)? {
@@ -266,7 +266,7 @@
         CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
     }
 
-    /// Retrieve the value of a given <string> property.
+    /// Returns the value of a given <string> property.
     pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
         let value = if let Some(bytes) = self.getprop(name)? {
             Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
@@ -276,7 +276,7 @@
         Ok(value)
     }
 
-    /// Retrieve the value of a given property as an array of cells.
+    /// Returns the value of a given property as an array of cells.
     pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
         if let Some(cells) = self.getprop(name)? {
             Ok(Some(CellIterator::new(cells)))
@@ -285,7 +285,7 @@
         }
     }
 
-    /// Retrieve the value of a given <u32> property.
+    /// Returns the value of a given <u32> property.
     pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
         let value = if let Some(bytes) = self.getprop(name)? {
             Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
@@ -295,7 +295,7 @@
         Ok(value)
     }
 
-    /// Retrieve the value of a given <u64> property.
+    /// Returns the value of a given <u64> property.
     pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
         let value = if let Some(bytes) = self.getprop(name)? {
             Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
@@ -305,7 +305,7 @@
         Ok(value)
     }
 
-    /// Retrieve the value of a given property.
+    /// Returns the value of a given property.
     pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
         if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
             Ok(Some(self.fdt.get_from_ptr(prop, len)?))
@@ -314,7 +314,7 @@
         }
     }
 
-    /// Return the pointer and size of the property named `name`, in a node at offset `offset`, in
+    /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
     /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
     fn getprop_internal(
         fdt: &'a Fdt,
@@ -347,7 +347,7 @@
         Ok(Some((prop.cast::<c_void>(), len)))
     }
 
-    /// Get reference to the containing device tree.
+    /// Returns reference to the containing device tree.
     pub fn fdt(&self) -> &Fdt {
         self.fdt
     }
@@ -412,7 +412,7 @@
 }
 
 impl<'a> FdtNodeMut<'a> {
-    /// Append a property name-value (possibly empty) pair to the given node.
+    /// Appends a property name-value (possibly empty) pair to the given node.
     pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
         // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
         let ret = unsafe {
@@ -428,7 +428,7 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Append a (address, size) pair property to the given node.
+    /// Appends a (address, size) pair property to the given node.
     pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
         // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
         let ret = unsafe {
@@ -445,7 +445,9 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Create or change a property name-value pair to the given node.
+    /// Sets a property name-value pair to the given node.
+    ///
+    /// This may create a new prop or replace existing value.
     pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
         // SAFETY: New value size is constrained to the DT totalsize
         //          (validated by underlying libfdt).
@@ -462,8 +464,10 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Replace the value of the given property with the given value, and ensure that the given
-    /// value has the same length as the current value length
+    /// Sets the value of the given property with the given value, and ensure that the given
+    /// value has the same length as the current value length.
+    ///
+    /// This can only be used to replace existing value.
     pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
         // SAFETY: fdt size is not altered
         let ret = unsafe {
@@ -479,19 +483,23 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Replace the value of the given (address, size) pair property with the given value, and
-    /// ensure that the given value has the same length as the current value length
+    /// Sets the value of the given (address, size) pair property with the given value, and
+    /// ensure that the given value has the same length as the current value length.
+    ///
+    /// This can only be used to replace existing value.
     pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
         let pair = [addr.to_be(), size.to_be()];
         self.setprop_inplace(name, pair.as_bytes())
     }
 
-    /// Create or change a flag-like empty property.
+    /// Sets a flag-like empty property.
+    ///
+    /// This may create a new prop or replace existing value.
     pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
         self.setprop(name, &[])
     }
 
-    /// Delete the given property.
+    /// Deletes the given property.
     pub fn delprop(&mut self, name: &CStr) -> Result<()> {
         // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
         // library locates the node's property. Removing the property may shift the offsets of
@@ -504,7 +512,7 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Overwrite the given property with FDT_NOP, effectively removing it from the DT.
+    /// Sets the given property with FDT_NOP, effectively removing it from the DT.
     pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
         // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
         // library locates the node's property.
@@ -515,7 +523,7 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Reduce the size of the given property to new_size
+    /// Trims the size of the given property to new_size.
     pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
         let (prop, len) =
             FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
@@ -540,12 +548,12 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Get reference to the containing device tree.
+    /// Returns reference to the containing device tree.
     pub fn fdt(&mut self) -> &mut Fdt {
         self.fdt
     }
 
-    /// Add a new subnode to the given node and return it as a FdtNodeMut on success.
+    /// Adds a new subnode to the given node and return it as a FdtNodeMut on success.
     pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
         // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
         let ret = unsafe {
@@ -562,7 +570,7 @@
         Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
     }
 
-    /// Returns the compatible node of the given name that is next after this node
+    /// Returns the compatible node of the given name that is next after this node.
     pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
         // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
         let ret = unsafe {
@@ -576,8 +584,8 @@
         Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
     }
 
-    /// Replace this node and its subtree with nop tags, effectively removing it from the tree, and
-    /// then return the next compatible node of the given name.
+    /// Deletes the node effectively by overwriting this node and its subtree with nop tags.
+    /// Returns the next compatible node of the given name.
     // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
     // The reason is that libfdt ensures that the node from where the search for the next
     // compatible node is started is always a valid one -- except for the special case of offset =
@@ -678,7 +686,7 @@
         unsafe { mem::transmute::<&mut [u8], &mut Self>(fdt) }
     }
 
-    /// Update this FDT from a slice containing another FDT
+    /// Updates this FDT from a slice containing another FDT.
     pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
         if self.buffer.len() < new_fdt.len() {
             Err(FdtError::NoSpace)
@@ -693,7 +701,7 @@
         }
     }
 
-    /// Make the whole slice containing the DT available to libfdt.
+    /// Unpacks the DT to cover the whole slice it is contained in.
     pub fn unpack(&mut self) -> Result<()> {
         // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
         // internal structures to make use of the whole self.fdt slice but performs no accesses
@@ -708,7 +716,7 @@
         fdt_err_expect_zero(ret)
     }
 
-    /// Pack the DT to take a minimum amount of memory.
+    /// Packs the DT to take a minimum amount of memory.
     ///
     /// Doesn't shrink the underlying memory slice.
     pub fn pack(&mut self) -> Result<()> {
@@ -752,22 +760,22 @@
         self.memory()?.next().ok_or(FdtError::NotFound)
     }
 
-    /// Retrieve the standard /chosen node.
+    /// Returns the standard /chosen node.
     pub fn chosen(&self) -> Result<Option<FdtNode>> {
         self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
     }
 
-    /// Retrieve the standard /chosen node as mutable.
+    /// Returns the standard /chosen node as mutable.
     pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
         self.node_mut(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
     }
 
-    /// Get the root node of the tree.
+    /// Returns the root node of the tree.
     pub fn root(&self) -> Result<FdtNode> {
         self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
     }
 
-    /// Find a tree node by its full path.
+    /// Returns a tree node by its full path.
     pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
         Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
     }
@@ -777,17 +785,17 @@
         CompatibleIterator::new(self, compatible)
     }
 
-    /// Get the mutable root node of the tree.
+    /// Returns the mutable root node of the tree.
     pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
         self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
     }
 
-    /// Find a mutable tree node by its full path.
+    /// Returns a mutable tree node by its full path.
     pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
         Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
     }
 
-    /// Return the device tree as a slice (may be smaller than the containing buffer).
+    /// Returns the device tree as a slice (may be smaller than the containing buffer).
     pub fn as_slice(&self) -> &[u8] {
         &self.buffer[..self.totalsize()]
     }
@@ -820,7 +828,7 @@
         self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
     }
 
-    /// Return a shared pointer to the device tree.
+    /// Returns a shared pointer to the device tree.
     pub fn as_ptr(&self) -> *const c_void {
         self.buffer.as_ptr().cast::<_>()
     }
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index c91519c..db65193 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -16,6 +16,7 @@
         "android.system.virtualization.payload-rust",
         "libandroid_logger",
         "libanyhow",
+        "libapkmanifest",
         "libavflog",
         "libapexutil_rust",
         "libapkverify",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index dd0ddbb..491d4de 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -34,12 +34,13 @@
 };
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
 use apkverify::{get_public_key_der, verify, V4Signature};
+use apkmanifest::get_manifest_info;
 use binder::Strong;
 use diced_open_dice::OwnedDiceArtifacts;
 use glob::glob;
 use itertools::sorted;
 use libc::VMADDR_CID_HOST;
-use log::{error, info};
+use log::{error, info, warn};
 use keystore2_crypto::ZVec;
 use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
@@ -424,7 +425,7 @@
     zipfuse.mount(
         MountForExec::Allowed,
         "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
-        Path::new("/dev/block/mapper/microdroid-apk"),
+        Path::new(DM_MOUNTED_APK_PATH),
         Path::new(VM_APK_CONTENTS_PATH),
         "microdroid_manager.apk.mounted".to_owned(),
     )?;
@@ -776,14 +777,25 @@
 
 fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
     let current_sdk = get_current_sdk()?;
-    if !root_hash_trustful {
+
+    let public_key_der = if !root_hash_trustful {
         verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
             "failed to verify {}",
             apk
-        )))
+        )))?
     } else {
-        get_public_key_der(apk, current_sdk)
-    }
+        get_public_key_der(apk, current_sdk)?
+    };
+
+    match get_manifest_info(apk) {
+        Ok(manifest_info) => {
+            // TODO (b/299591171): Do something with this info
+            info!("Manifest info is {manifest_info:?}")
+        }
+        Err(e) => warn!("Failed to read manifest info from APK: {e:?}"),
+    };
+
+    Ok(public_key_der)
 }
 
 fn get_current_sdk() -> Result<u32> {
diff --git a/libs/dice_policy/Android.bp b/secretkeeper/dice_policy/Android.bp
similarity index 100%
rename from libs/dice_policy/Android.bp
rename to secretkeeper/dice_policy/Android.bp
diff --git a/libs/dice_policy/src/lib.rs b/secretkeeper/dice_policy/src/lib.rs
similarity index 100%
rename from libs/dice_policy/src/lib.rs
rename to secretkeeper/dice_policy/src/lib.rs
diff --git a/libs/dice_policy/testdata/composbcc b/secretkeeper/dice_policy/testdata/composbcc
similarity index 100%
rename from libs/dice_policy/testdata/composbcc
rename to secretkeeper/dice_policy/testdata/composbcc
Binary files differ
diff --git a/service_vm/requests/src/rkp.rs b/service_vm/requests/src/rkp.rs
index 2d80f13..933737c 100644
--- a/service_vm/requests/src/rkp.rs
+++ b/service_vm/requests/src/rkp.rs
@@ -25,7 +25,7 @@
 use ciborium::{cbor, value::Value};
 use core::result;
 use coset::{iana, AsCborValue, CoseSign1, CoseSign1Builder, HeaderBuilder};
-use diced_open_dice::{kdf, keypair_from_seed, sign, DiceArtifacts, PrivateKey};
+use diced_open_dice::{derive_cdi_leaf_priv, kdf, sign, DiceArtifacts, PrivateKey};
 use log::error;
 use service_vm_comm::{EcdsaP256KeyPair, GenerateCertificateRequestParams, RequestProcessingError};
 use zeroize::Zeroizing;
@@ -128,11 +128,6 @@
     Ok(signed_data)
 }
 
-fn derive_cdi_leaf_priv(dice_artifacts: &dyn DiceArtifacts) -> diced_open_dice::Result<PrivateKey> {
-    let (_, private_key) = keypair_from_seed(dice_artifacts.cdi_attest())?;
-    Ok(private_key)
-}
-
 fn sign_message(message: &[u8], private_key: &PrivateKey) -> Result<Vec<u8>> {
     Ok(sign(message, private_key.as_array())
         .map_err(|e| {