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| {