Merge changes Idad0ce1e,I84f0ca0f into main
* changes:
pvmfw: Validate config header entries when Config::new()
pvmfw: Refactor to add config::Range
diff --git a/Android.bp b/Android.bp
index 2f6fc20..64d193a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -64,3 +64,9 @@
},
},
}
+
+genrule_defaults {
+ name: "dts_to_dtb",
+ tools: ["dtc"],
+ cmd: "$(location dtc) -I dts -O dtb $(in) -o $(out)",
+}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index adf6309..1410534 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -31,6 +31,9 @@
},
{
"name": "libdice_policy.test"
+ },
+ {
+ "name": "libapkzip.test"
}
],
"avf-postsubmit": [
@@ -81,6 +84,9 @@
"path": "packages/modules/Virtualization/libs/devicemapper"
},
{
+ "path": "packages/modules/Virtualization/libs/dice"
+ },
+ {
"path": "packages/modules/Virtualization/libs/libfdt"
},
{
@@ -99,6 +105,9 @@
"path": "packages/modules/Virtualization/rialto"
},
{
+ "path": "packages/modules/Virtualization/service_vm/requests"
+ },
+ {
"path": "packages/modules/Virtualization/vm"
},
{
diff --git a/apex/empty-payload-apk/Android.bp b/apex/empty-payload-apk/Android.bp
index 70e6754..8bd138f 100644
--- a/apex/empty-payload-apk/Android.bp
+++ b/apex/empty-payload-apk/Android.bp
@@ -9,8 +9,8 @@
apex_available: ["com.android.virt"],
sdk_version: "system_current",
jni_uses_platform_apis: true,
- min_sdk_version: "UpsideDownCake",
- target_sdk_version: "UpsideDownCake",
+ min_sdk_version: "34",
+ target_sdk_version: "34",
compile_multilib: "first",
stl: "none",
}
diff --git a/compos/Android.bp b/compos/Android.bp
index 19123dd..b840506 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -14,6 +14,7 @@
"libanyhow",
"libbinder_rs",
"libcompos_common",
+ "libhex",
"liblibc",
"liblog_rust",
"libminijail_rust",
diff --git a/compos/benchmark/Android.bp b/compos/benchmark/Android.bp
index dc0c01c..93927a2 100644
--- a/compos/benchmark/Android.bp
+++ b/compos/benchmark/Android.bp
@@ -13,7 +13,7 @@
"androidx.test.ext.junit",
"MicrodroidDeviceTestHelper",
"MicrodroidTestHelper",
- "truth-prebuilt",
+ "truth",
],
sdk_version: "test_current",
use_embedded_native_libs: true,
diff --git a/compos/src/artifact_signer.rs b/compos/src/artifact_signer.rs
index 76da00a..bfd886e 100644
--- a/compos/src/artifact_signer.rs
+++ b/compos/src/artifact_signer.rs
@@ -53,7 +53,7 @@
let file = File::open(path).with_context(|| format!("Opening {}", path.display()))?;
let digest = fsverity::measure(file.as_fd())?;
- let digest = to_hex_string(&digest);
+ let digest = hex::encode(digest);
self.file_digests.push((target_path.to_owned(), digest));
Ok(())
@@ -82,7 +82,3 @@
Ok(())
}
}
-
-fn to_hex_string(buf: &[u8]) -> String {
- buf.iter().map(|b| format!("{:02x}", b)).collect()
-}
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/bssl/error/src/code.rs b/libs/bssl/error/src/code.rs
index 7fb36c4..9b661e9 100644
--- a/libs/bssl/error/src/code.rs
+++ b/libs/bssl/error/src/code.rs
@@ -91,6 +91,12 @@
InvalidNonce,
}
+impl From<CipherError> for ReasonCode {
+ fn from(e: CipherError) -> ReasonCode {
+ ReasonCode::Cipher(e)
+ }
+}
+
impl fmt::Display for CipherError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "An error occurred in a Cipher function: {self:?}")
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 80398c0..3766c41 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -68,4 +68,5 @@
EVP_AEAD_CTX_seal,
HKDF,
HMAC,
+ RAND_bytes,
}
diff --git a/libs/bssl/src/aead.rs b/libs/bssl/src/aead.rs
index a7d03b9..e0c9fbb 100644
--- a/libs/bssl/src/aead.rs
+++ b/libs/bssl/src/aead.rs
@@ -23,8 +23,13 @@
};
use core::ptr::NonNull;
+/// BoringSSL spec recommends to use 12-byte nonces.
+///
+/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_aead_aes_256_gcm
+pub const AES_GCM_NONCE_LENGTH: usize = 12;
+
/// Magic value indicating that the default tag length for an AEAD should be used to
-/// initialize `AeadCtx`.
+/// initialize `AeadContext`.
const AEAD_DEFAULT_TAG_LENGTH: usize = EVP_AEAD_DEFAULT_TAG_LENGTH as usize;
/// Represents an AEAD algorithm.
@@ -60,12 +65,12 @@
}
/// Represents an AEAD algorithm configuration.
-pub struct AeadCtx {
+pub struct AeadContext {
ctx: NonNull<EVP_AEAD_CTX>,
aead: Aead,
}
-impl Drop for AeadCtx {
+impl Drop for AeadContext {
fn drop(&mut self) {
// SAFETY: It is safe because the pointer has been created with `EVP_AEAD_CTX_new`
// and isn't used after this.
@@ -73,8 +78,8 @@
}
}
-impl AeadCtx {
- /// Creates a new `AeadCtx` with the given `Aead` algorithm, `key` and `tag_len`.
+impl AeadContext {
+ /// Creates a new `AeadContext` with the given `Aead` algorithm, `key` and `tag_len`.
///
/// The default tag length will be used if `tag_len` is None.
pub fn new(aead: Aead, key: &[u8], tag_len: Option<usize>) -> Result<Self> {
@@ -153,7 +158,7 @@
out.get(0..out_len).ok_or(to_call_failed_error(ApiName::EVP_AEAD_CTX_open))
}
- /// Returns the `Aead` represented by this `AeadCtx`.
+ /// Returns the `Aead` represented by this `AeadContext`.
pub fn aead(&self) -> Aead {
self.aead
}
diff --git a/libs/bssl/src/lib.rs b/libs/bssl/src/lib.rs
index 898e16c..709e8ad 100644
--- a/libs/bssl/src/lib.rs
+++ b/libs/bssl/src/lib.rs
@@ -25,13 +25,15 @@
mod err;
mod hkdf;
mod hmac;
+mod rand;
mod util;
pub use bssl_avf_error::{ApiName, CipherError, Error, ReasonCode, Result};
-pub use aead::{Aead, AeadCtx};
+pub use aead::{Aead, AeadContext, AES_GCM_NONCE_LENGTH};
pub use cbb::CbbFixed;
pub use digest::Digester;
pub use ec_key::{EcKey, ZVec};
pub use hkdf::hkdf;
pub use hmac::hmac_sha256;
+pub use rand::rand_bytes;
diff --git a/libs/bssl/src/rand.rs b/libs/bssl/src/rand.rs
new file mode 100644
index 0000000..9343284
--- /dev/null
+++ b/libs/bssl/src/rand.rs
@@ -0,0 +1,26 @@
+// 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.
+
+//! Wrappers of the randon number generations functions in BoringSSL rand.h.
+
+use crate::util::check_int_result;
+use bssl_avf_error::{ApiName, Result};
+use bssl_ffi::RAND_bytes;
+
+/// Fills the given `dest` with random data.
+pub fn rand_bytes(dest: &mut [u8]) -> Result<()> {
+ // SAFETY: This function only writes to the given buffer within its bounds.
+ let ret = unsafe { RAND_bytes(dest.as_mut_ptr(), dest.len()) };
+ check_int_result(ret, ApiName::RAND_bytes)
+}
diff --git a/libs/bssl/tests/aead_test.rs b/libs/bssl/tests/aead_test.rs
index 8ac3f12..8bdb0e7 100644
--- a/libs/bssl/tests/aead_test.rs
+++ b/libs/bssl/tests/aead_test.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use bssl_avf::{Aead, AeadCtx, ApiName, CipherError, Error, ReasonCode, Result};
+use bssl_avf::{Aead, AeadContext, ApiName, CipherError, Error, ReasonCode, Result};
/// The following vectors are generated randomly with:
/// `hexdump -vn32 -e'32/1 "0x%02x, " 1 "\n"' /dev/urandom`
@@ -38,7 +38,7 @@
let tag_len = None;
let ad = &[];
- let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
let mut out = vec![0u8; ciphertext.len()];
let plaintext = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE1, ad, &mut out)?;
@@ -50,7 +50,7 @@
#[test]
fn aes_256_gcm_fails_to_encrypt_with_invalid_nonce() -> Result<()> {
let tag_len = None;
- let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
let nonce = &[];
let ad = &[];
let mut out = vec![0u8; MESSAGE.len() + aead_ctx.aead().max_overhead()];
@@ -71,7 +71,7 @@
let tag_len = None;
let ad = &[];
- let aead_ctx2 = AeadCtx::new(Aead::aes_256_gcm(), &KEY2, tag_len)?;
+ let aead_ctx2 = AeadContext::new(Aead::aes_256_gcm(), &KEY2, tag_len)?;
let mut plaintext = vec![0u8; ciphertext.len()];
let err = aead_ctx2.open(&ciphertext, &AES_256_GCM_NONCE1, ad, &mut plaintext).unwrap_err();
@@ -88,7 +88,7 @@
let tag_len = None;
let ad2 = &[1];
- let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
let mut plaintext = vec![0u8; ciphertext.len()];
let err = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE1, ad2, &mut plaintext).unwrap_err();
@@ -105,7 +105,7 @@
let tag_len = None;
let ad = &[];
- let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
let mut plaintext = vec![0u8; ciphertext.len()];
let err = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE2, ad, &mut plaintext).unwrap_err();
@@ -123,7 +123,7 @@
let tag_len = None;
let ad = &[];
- let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
let mut plaintext = vec![0u8; ciphertext.len()];
let err = aead_ctx.open(&ciphertext, &AES_256_GCM_NONCE1, ad, &mut plaintext).unwrap_err();
@@ -136,7 +136,7 @@
fn aes_256_gcm_encrypt(message: &[u8]) -> Result<Vec<u8>> {
let tag_len = None;
- let aead_ctx = AeadCtx::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), &KEY1, tag_len)?;
let mut out = vec![0u8; message.len() + aead_ctx.aead().max_overhead()];
assert_eq!(aead_ctx.aead().nonce_length(), AES_256_GCM_NONCE1.len());
diff --git a/libs/dice/OWNERS b/libs/dice/OWNERS
new file mode 100644
index 0000000..fbc501d
--- /dev/null
+++ b/libs/dice/OWNERS
@@ -0,0 +1 @@
+ascull@google.com
diff --git a/libs/dice/TEST_MAPPING b/libs/dice/TEST_MAPPING
new file mode 100644
index 0000000..2045ba5
--- /dev/null
+++ b/libs/dice/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+ "postsubmit": [
+ {
+ "name": "libdiced_open_dice.integration_test"
+ },
+ {
+ "name": "libdiced_open_dice_nostd.integration_test"
+ },
+ {
+ "name": "libopen_dice_cbor_bindgen_test"
+ },
+ {
+ "name": "libopen_dice_android_bindgen_test"
+ },
+ {
+ "name": "libdiced_sample_inputs.integration_test"
+ },
+ {
+ "name": "libdiced_sample_inputs_nostd.integration_test"
+ }
+ ]
+}
diff --git a/libs/dice/open_dice/Android.bp b/libs/dice/open_dice/Android.bp
new file mode 100644
index 0000000..646080d
--- /dev/null
+++ b/libs/dice/open_dice/Android.bp
@@ -0,0 +1,258 @@
+package {
+ default_visibility: [":__subpackages__"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "libdiced_open_dice_defaults",
+ crate_name: "diced_open_dice",
+ defaults: ["avf_build_flags_rust"],
+ srcs: ["src/lib.rs"],
+}
+
+rust_library_rlib {
+ name: "libdiced_open_dice_nostd",
+ defaults: ["libdiced_open_dice_defaults"],
+ rustlibs: [
+ "libopen_dice_android_bindgen_nostd",
+ "libopen_dice_cbor_bindgen_nostd",
+ "libzeroize_nostd",
+ ],
+ features: [
+ "alloc",
+ ],
+ whole_static_libs: [
+ "libopen_dice_cbor",
+ "libcrypto_baremetal",
+ ],
+ visibility: [
+ "//packages/modules/Virtualization:__subpackages__",
+ ],
+}
+
+rust_library {
+ name: "libdiced_open_dice",
+ defaults: ["libdiced_open_dice_defaults"],
+ vendor_available: true,
+ rustlibs: [
+ "libopen_dice_android_bindgen",
+ "libopen_dice_cbor_bindgen",
+ "libzeroize",
+ ],
+ features: [
+ "alloc",
+ "std",
+ ],
+ shared_libs: [
+ "libcrypto",
+ ],
+ static_libs: [
+ "libopen_dice_cbor",
+ ],
+ whole_static_libs: [
+ "libopen_dice_android",
+ ],
+ visibility: [
+ "//packages/modules/Virtualization:__subpackages__",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+}
+
+rust_defaults {
+ name: "libdiced_open_dice_test_defaults",
+ crate_name: "diced_open_dice_test",
+ srcs: ["tests/*.rs"],
+ test_suites: ["general-tests"],
+}
+
+rust_test {
+ name: "libdiced_open_dice.integration_test",
+ defaults: ["libdiced_open_dice_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice",
+ ],
+}
+
+rust_test {
+ name: "libdiced_open_dice_nostd.integration_test",
+ defaults: ["libdiced_open_dice_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice_nostd",
+ ],
+}
+
+rust_defaults {
+ name: "libopen_dice_bindgen_nostd.rust_defaults",
+ bindgen_flags: [
+ "--use-core",
+ "--ctypes-prefix=core::ffi",
+ "--raw-line=#![no_std]",
+ ],
+ no_stdlibs: true,
+ prefer_rlib: true,
+ stdlibs: [
+ "libcore.rust_sysroot",
+ "libcompiler_builtins.rust_sysroot",
+ ],
+ target: {
+ musl: {
+ enabled: false,
+ },
+ glibc: {
+ enabled: false,
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+rust_defaults {
+ name: "libopen_dice.rust_defaults",
+ host_supported: true,
+ vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ "com.android.virt",
+ ],
+}
+
+rust_defaults {
+ name: "libopen_dice_cbor_bindgen.rust_defaults",
+ wrapper_src: "bindgen/dice.h",
+ crate_name: "open_dice_cbor_bindgen",
+ source_stem: "bindings",
+ bindgen_flags: [
+ "--rustified-enum DiceConfigType",
+ "--rustified-enum DiceMode",
+ "--rustified-enum DiceResult",
+
+ // By generating only essential functions, we can make bindings concise and
+ // optimize compilation time.
+ "--allowlist-function=DiceDeriveCdiPrivateKeySeed",
+ "--allowlist-function=DiceDeriveCdiCertificateId",
+ "--allowlist-function=DiceMainFlow",
+ "--allowlist-function=DiceHash",
+ "--allowlist-function=DiceKdf",
+ "--allowlist-function=DiceKeypairFromSeed",
+ "--allowlist-function=DiceSign",
+ "--allowlist-function=DiceVerify",
+ "--allowlist-function=DiceGenerateCertificate",
+
+ // We also need some constants in addition to the functions.
+ "--allowlist-var=DICE_CDI_SIZE",
+ "--allowlist-var=DICE_HASH_SIZE",
+ "--allowlist-var=DICE_HIDDEN_SIZE",
+ "--allowlist-var=DICE_INLINE_CONFIG_SIZE",
+ "--allowlist-var=DICE_PRIVATE_KEY_SEED_SIZE",
+ "--allowlist-var=DICE_ID_SIZE",
+ "--allowlist-var=DICE_PUBLIC_KEY_SIZE",
+ "--allowlist-var=DICE_PRIVATE_KEY_SIZE",
+ "--allowlist-var=DICE_SIGNATURE_SIZE",
+ ],
+}
+
+rust_bindgen {
+ name: "libopen_dice_cbor_bindgen",
+ defaults: [
+ "libopen_dice.rust_defaults",
+ "libopen_dice_cbor_bindgen.rust_defaults",
+ ],
+ whole_static_libs: ["libopen_dice_cbor"],
+}
+
+rust_bindgen {
+ name: "libopen_dice_cbor_bindgen_nostd",
+ defaults: [
+ "libopen_dice_cbor_bindgen.rust_defaults",
+ "libopen_dice_bindgen_nostd.rust_defaults",
+ ],
+ whole_static_libs: ["libopen_dice_cbor_baremetal"],
+}
+
+rust_defaults {
+ name: "libopen_dice_android_bindgen.rust_defaults",
+ wrapper_src: "bindgen/android.h",
+ crate_name: "open_dice_android_bindgen",
+ source_stem: "bindings",
+ bindgen_flags: [
+ // By generating only essential functions, we can make bindings concise and
+ // optimize compilation time.
+ "--allowlist-function=DiceAndroidFormatConfigDescriptor",
+ "--allowlist-function=DiceAndroidMainFlow",
+ "--allowlist-function=DiceAndroidHandoverMainFlow",
+ "--allowlist-function=DiceAndroidHandoverParse",
+
+ // We also need some constants in addition to the functions.
+ "--allowlist-var=DICE_ANDROID_CONFIG_.*",
+
+ // Prevent DiceInputValues from being generated a second time and
+ // import it instead from open_dice_cbor_bindgen.
+ "--blocklist-type=DiceInputValues_",
+ "--blocklist-type=DiceInputValues",
+ "--raw-line",
+ "pub use open_dice_cbor_bindgen::DiceInputValues;",
+
+ // Prevent DiceResult from being generated a second time and
+ // import it instead from open_dice_cbor_bindgen.
+ "--blocklist-type=DiceResult",
+ "--raw-line",
+ "pub use open_dice_cbor_bindgen::DiceResult;",
+ ],
+
+}
+
+rust_bindgen {
+ name: "libopen_dice_android_bindgen",
+ defaults: [
+ "libopen_dice.rust_defaults",
+ "libopen_dice_android_bindgen.rust_defaults",
+ ],
+ rustlibs: [
+ "libopen_dice_cbor_bindgen",
+ ],
+ whole_static_libs: ["libopen_dice_android"],
+}
+
+rust_bindgen {
+ name: "libopen_dice_android_bindgen_nostd",
+ defaults: [
+ "libopen_dice_android_bindgen.rust_defaults",
+ "libopen_dice_bindgen_nostd.rust_defaults",
+ ],
+ rustlibs: [
+ "libopen_dice_cbor_bindgen_nostd",
+ ],
+ whole_static_libs: ["libopen_dice_android_baremetal"],
+}
+
+rust_test {
+ name: "libopen_dice_cbor_bindgen_test",
+ srcs: [
+ ":libopen_dice_cbor_bindgen",
+ ],
+ crate_name: "open_dice_cbor_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
+
+rust_test {
+ name: "libopen_dice_android_bindgen_test",
+ srcs: [
+ ":libopen_dice_android_bindgen",
+ ],
+ crate_name: "open_dice_android_bindgen_test",
+ rustlibs: [
+ "libopen_dice_cbor_bindgen",
+ ],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
diff --git a/libs/dice/open_dice/bindgen/android.h b/libs/dice/open_dice/bindgen/android.h
new file mode 100644
index 0000000..18f6476
--- /dev/null
+++ b/libs/dice/open_dice/bindgen/android.h
@@ -0,0 +1,17 @@
+// Copyright 2021 Google LLC
+//
+// 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
+//
+// https://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 <dice/android.h>
diff --git a/libs/dice/open_dice/bindgen/dice.h b/libs/dice/open_dice/bindgen/dice.h
new file mode 100644
index 0000000..47fe911
--- /dev/null
+++ b/libs/dice/open_dice/bindgen/dice.h
@@ -0,0 +1,18 @@
+// Copyright 2021 Google LLC
+//
+// 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
+//
+// https://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 <dice/dice.h>
+#include <dice/ops.h>
diff --git a/libs/dice/open_dice/src/bcc.rs b/libs/dice/open_dice/src/bcc.rs
new file mode 100644
index 0000000..199e1a9
--- /dev/null
+++ b/libs/dice/open_dice/src/bcc.rs
@@ -0,0 +1,223 @@
+// 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.
+
+//! This module mirrors the content in open-dice/include/dice/android.h
+
+use crate::dice::{Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE};
+use crate::error::{check_result, DiceError, Result};
+use open_dice_android_bindgen::{
+ DiceAndroidConfigValues, DiceAndroidFormatConfigDescriptor, DiceAndroidHandoverMainFlow,
+ DiceAndroidHandoverParse, DiceAndroidMainFlow, DICE_ANDROID_CONFIG_COMPONENT_NAME,
+ DICE_ANDROID_CONFIG_COMPONENT_VERSION, DICE_ANDROID_CONFIG_RESETTABLE,
+ DICE_ANDROID_CONFIG_SECURITY_VERSION,
+};
+use std::{ffi::CStr, ptr};
+
+/// Contains the input values used to construct the Android Profile for DICE
+/// configuration descriptor.
+#[derive(Default, Debug)]
+pub struct DiceConfigValues<'a> {
+ /// Name of the component.
+ pub component_name: Option<&'a CStr>,
+ /// Version of the component.
+ pub component_version: Option<u64>,
+ /// Whether the key changes on factory reset.
+ pub resettable: bool,
+ /// Monotonically increasing version of the component.
+ pub security_version: Option<u64>,
+}
+
+/// Formats a configuration descriptor following the Android Profile for DICE specification.
+/// See https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/android.md.
+pub fn bcc_format_config_descriptor(values: &DiceConfigValues, buffer: &mut [u8]) -> Result<usize> {
+ let mut configs = 0;
+
+ let component_name = values.component_name.map_or(ptr::null(), |name| {
+ configs |= DICE_ANDROID_CONFIG_COMPONENT_NAME;
+ name.as_ptr()
+ });
+ let component_version = values.component_version.map_or(0, |version| {
+ configs |= DICE_ANDROID_CONFIG_COMPONENT_VERSION;
+ version
+ });
+ if values.resettable {
+ configs |= DICE_ANDROID_CONFIG_RESETTABLE;
+ }
+ let security_version = values.security_version.map_or(0, |version| {
+ configs |= DICE_ANDROID_CONFIG_SECURITY_VERSION;
+ version
+ });
+
+ let values =
+ DiceAndroidConfigValues { configs, component_name, component_version, security_version };
+
+ let mut buffer_size = 0;
+ check_result(
+ // SAFETY: The function writes to the buffer, within the given bounds, and only reads the
+ // input values. It writes its result to buffer_size.
+ unsafe {
+ DiceAndroidFormatConfigDescriptor(
+ &values,
+ buffer.len(),
+ buffer.as_mut_ptr(),
+ &mut buffer_size,
+ )
+ },
+ buffer_size,
+ )?;
+ Ok(buffer_size)
+}
+
+/// Executes the main Android DICE flow.
+///
+/// Given a full set of input values along with the current DICE chain and CDI values,
+/// computes the next CDI values and matching updated DICE chain.
+pub fn bcc_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ current_chain: &[u8],
+ input_values: &InputValues,
+ next_cdi_values: &mut CdiValues,
+ next_chain: &mut [u8],
+) -> Result<usize> {
+ let mut next_chain_size = 0;
+ check_result(
+ // SAFETY: `DiceAndroidMainFlow` only reads the `current_chain` and CDI values and writes
+ // to `next_chain` and next CDI values within its bounds. It also reads `input_values` as a
+ // constant input and doesn't store any pointer.
+ // The first argument can be null and is not used in the current implementation.
+ unsafe {
+ DiceAndroidMainFlow(
+ ptr::null_mut(), // context
+ current_cdi_attest.as_ptr(),
+ current_cdi_seal.as_ptr(),
+ current_chain.as_ptr(),
+ current_chain.len(),
+ input_values.as_ptr(),
+ next_chain.len(),
+ next_chain.as_mut_ptr(),
+ &mut next_chain_size,
+ next_cdi_values.cdi_attest.as_mut_ptr(),
+ next_cdi_values.cdi_seal.as_mut_ptr(),
+ )
+ },
+ next_chain_size,
+ )?;
+ Ok(next_chain_size)
+}
+
+/// Executes the main Android DICE handover flow.
+///
+/// A handover combines the DICE chain and CDIs in a single CBOR object.
+/// This function takes the current boot stage's handover bundle and produces a
+/// bundle for the next stage.
+pub fn bcc_handover_main_flow(
+ current_handover: &[u8],
+ input_values: &InputValues,
+ next_handover: &mut [u8],
+) -> Result<usize> {
+ let mut next_handover_size = 0;
+ check_result(
+ // SAFETY: The function only reads `current_handover` and writes to `next_handover`
+ // within its bounds,
+ // It also reads `input_values` as a constant input and doesn't store any pointer.
+ // The first argument can be null and is not used in the current implementation.
+ unsafe {
+ DiceAndroidHandoverMainFlow(
+ ptr::null_mut(), // context
+ current_handover.as_ptr(),
+ current_handover.len(),
+ input_values.as_ptr(),
+ next_handover.len(),
+ next_handover.as_mut_ptr(),
+ &mut next_handover_size,
+ )
+ },
+ next_handover_size,
+ )?;
+
+ Ok(next_handover_size)
+}
+
+/// An Android DICE handover object combines the DICE chain and CDIs in a single CBOR object.
+/// This struct is used as return of the function `android_dice_handover_parse`, its lifetime is
+/// tied to the lifetime of the raw handover slice.
+#[derive(Debug)]
+pub struct BccHandover<'a> {
+ /// Attestation CDI.
+ cdi_attest: &'a [u8; CDI_SIZE],
+ /// Sealing CDI.
+ cdi_seal: &'a [u8; CDI_SIZE],
+ /// DICE chain.
+ bcc: Option<&'a [u8]>,
+}
+
+impl<'a> DiceArtifacts for BccHandover<'a> {
+ fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
+ self.cdi_attest
+ }
+
+ fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
+ self.cdi_seal
+ }
+
+ fn bcc(&self) -> Option<&[u8]> {
+ self.bcc
+ }
+}
+
+/// This function parses the `handover` to extracts the DICE chain and CDIs.
+/// The lifetime of the returned `DiceAndroidHandover` is tied to the given `handover` slice.
+pub fn bcc_handover_parse(handover: &[u8]) -> Result<BccHandover> {
+ let mut cdi_attest: *const u8 = ptr::null();
+ let mut cdi_seal: *const u8 = ptr::null();
+ let mut chain: *const u8 = ptr::null();
+ let mut chain_size = 0;
+ check_result(
+ // SAFETY: The `handover` is only read and never stored and the returned pointers should
+ // all point within the address range of the `handover` or be NULL.
+ unsafe {
+ DiceAndroidHandoverParse(
+ handover.as_ptr(),
+ handover.len(),
+ &mut cdi_attest,
+ &mut cdi_seal,
+ &mut chain,
+ &mut chain_size,
+ )
+ },
+ chain_size,
+ )?;
+ let cdi_attest = sub_slice(handover, cdi_attest, CDI_SIZE)?;
+ let cdi_seal = sub_slice(handover, cdi_seal, CDI_SIZE)?;
+ let bcc = sub_slice(handover, chain, chain_size).ok();
+ Ok(BccHandover {
+ cdi_attest: cdi_attest.try_into().map_err(|_| DiceError::PlatformError)?,
+ cdi_seal: cdi_seal.try_into().map_err(|_| DiceError::PlatformError)?,
+ bcc,
+ })
+}
+
+/// Gets a slice the `addr` points to and of length `len`.
+/// The slice should be contained in the buffer.
+fn sub_slice(buffer: &[u8], addr: *const u8, len: usize) -> Result<&[u8]> {
+ if addr.is_null() || !buffer.as_ptr_range().contains(&addr) {
+ return Err(DiceError::PlatformError);
+ }
+ // SAFETY: This is safe because addr is not null and is within the range of the buffer.
+ let start: usize = unsafe {
+ addr.offset_from(buffer.as_ptr()).try_into().map_err(|_| DiceError::PlatformError)?
+ };
+ start.checked_add(len).and_then(|end| buffer.get(start..end)).ok_or(DiceError::PlatformError)
+}
diff --git a/libs/dice/open_dice/src/dice.rs b/libs/dice/open_dice/src/dice.rs
new file mode 100644
index 0000000..e42e373
--- /dev/null
+++ b/libs/dice/open_dice/src/dice.rs
@@ -0,0 +1,287 @@
+// 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.
+
+//! Structs and functions about the types used in DICE.
+//! This module mirrors the content in open-dice/include/dice/dice.h
+
+use crate::error::{check_result, Result};
+pub use open_dice_cbor_bindgen::DiceMode;
+use open_dice_cbor_bindgen::{
+ DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
+ DiceMainFlow, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE,
+ DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, DICE_PRIVATE_KEY_SIZE,
+ DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
+};
+use std::{marker::PhantomData, ptr};
+use zeroize::{Zeroize, ZeroizeOnDrop};
+
+/// The size of a DICE hash.
+pub const HASH_SIZE: usize = DICE_HASH_SIZE as usize;
+/// The size of the DICE hidden value.
+pub const HIDDEN_SIZE: usize = DICE_HIDDEN_SIZE as usize;
+/// The size of a DICE inline config.
+const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
+/// The size of a CDI.
+pub const CDI_SIZE: usize = DICE_CDI_SIZE as usize;
+/// The size of a private key seed.
+pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
+/// The size of a private key.
+pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
+/// The size of a public key.
+pub const PUBLIC_KEY_SIZE: usize = DICE_PUBLIC_KEY_SIZE as usize;
+/// The size of a signature.
+pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
+/// The size of an ID.
+pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
+
+/// Array type of hashes used by DICE.
+pub type Hash = [u8; HASH_SIZE];
+/// Array type of additional input.
+pub type Hidden = [u8; HIDDEN_SIZE];
+/// Array type of inline configuration values.
+pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
+/// Array type of CDIs.
+pub type Cdi = [u8; CDI_SIZE];
+/// Array type of the public key.
+pub type PublicKey = [u8; PUBLIC_KEY_SIZE];
+/// Array type of the signature.
+pub type Signature = [u8; SIGNATURE_SIZE];
+/// Array type of DICE ID.
+pub type DiceId = [u8; ID_SIZE];
+
+/// A trait for types that represent Dice artifacts, which include:
+///
+/// - Attestation CDI
+/// - Sealing CDI
+/// - Boot Certificate Chain
+///
+/// Types that implement this trait provide an access these artifacts.
+pub trait DiceArtifacts {
+ /// Returns a reference to the attestation CDI.
+ fn cdi_attest(&self) -> &[u8; CDI_SIZE];
+
+ /// Returns a reference to the sealing CDI.
+ fn cdi_seal(&self) -> &[u8; CDI_SIZE];
+
+ /// Returns a reference to the Boot Certificate Chain, if present.
+ fn bcc(&self) -> Option<&[u8]>;
+}
+
+/// TODO(b/268587826): Clean up the memory cache after zeroing out the memory
+/// for sensitive data like CDI values and private key.
+/// CDI Values.
+#[derive(Debug, Zeroize, ZeroizeOnDrop, Default)]
+pub struct CdiValues {
+ /// Attestation CDI.
+ pub cdi_attest: [u8; CDI_SIZE],
+ /// Sealing CDI.
+ pub cdi_seal: [u8; CDI_SIZE],
+}
+
+/// Private key seed. The data is zeroed out when the struct is dropped.
+#[derive(Zeroize, ZeroizeOnDrop, Default)]
+pub struct PrivateKeySeed([u8; PRIVATE_KEY_SEED_SIZE]);
+
+impl PrivateKeySeed {
+ /// Returns an array reference of the private key seed.
+ pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SEED_SIZE] {
+ &self.0
+ }
+
+ /// Returns a mutable pointer to the slice buffer of the private key seed.
+ pub fn as_mut_ptr(&mut self) -> *mut u8 {
+ self.0.as_mut_ptr()
+ }
+}
+
+/// Private key. The data is zeroed out when the struct is dropped.
+#[derive(Zeroize, ZeroizeOnDrop)]
+pub struct PrivateKey([u8; PRIVATE_KEY_SIZE]);
+
+impl Default for PrivateKey {
+ /// Creates a new `PrivateKey` instance with all bytes set to 0.
+ ///
+ /// Since the size of the private key array is too large to be initialized
+ /// with a default value, this implementation sets all the bytes in the array
+ /// to 0 using the `[0u8; PRIVATE_KEY_SIZE]` syntax.
+ fn default() -> Self {
+ Self([0u8; PRIVATE_KEY_SIZE])
+ }
+}
+
+impl PrivateKey {
+ /// Returns an array reference of the private key.
+ pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SIZE] {
+ &self.0
+ }
+
+ /// Returns a mutable pointer to the slice buffer of the private key.
+ pub fn as_mut_ptr(&mut self) -> *mut u8 {
+ self.0.as_mut_ptr()
+ }
+}
+
+/// Configuration descriptor for DICE input values.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Config<'a> {
+ /// Reference to an inline descriptor.
+ Inline(&'a InlineConfig),
+ /// Reference to a free form descriptor that will be hashed by the implementation.
+ Descriptor(&'a [u8]),
+}
+
+impl Config<'_> {
+ fn dice_config_type(&self) -> DiceConfigType {
+ match self {
+ Self::Inline(_) => DiceConfigType::kDiceConfigTypeInline,
+ Self::Descriptor(_) => DiceConfigType::kDiceConfigTypeDescriptor,
+ }
+ }
+
+ fn inline_config(&self) -> InlineConfig {
+ match self {
+ Self::Inline(inline) => **inline,
+ Self::Descriptor(_) => [0u8; INLINE_CONFIG_SIZE],
+ }
+ }
+
+ fn descriptor_ptr(&self) -> *const u8 {
+ match self {
+ Self::Descriptor(descriptor) => descriptor.as_ptr(),
+ _ => ptr::null(),
+ }
+ }
+
+ fn descriptor_size(&self) -> usize {
+ match self {
+ Self::Descriptor(descriptor) => descriptor.len(),
+ _ => 0,
+ }
+ }
+}
+
+/// Wrap of `DiceInputValues`.
+#[derive(Clone, Debug)]
+pub struct InputValues<'a> {
+ dice_inputs: DiceInputValues,
+ // DiceInputValues contains a pointer to the separate config descriptor, which must therefore
+ // outlive it. Make sure the borrow checker can enforce that.
+ config_descriptor: PhantomData<&'a [u8]>,
+}
+
+impl<'a> InputValues<'a> {
+ /// Creates a new `InputValues`.
+ pub fn new(
+ code_hash: Hash,
+ config: Config<'a>,
+ authority_hash: Hash,
+ mode: DiceMode,
+ hidden: Hidden,
+ ) -> Self {
+ Self {
+ dice_inputs: DiceInputValues {
+ code_hash,
+ code_descriptor: ptr::null(),
+ code_descriptor_size: 0,
+ config_type: config.dice_config_type(),
+ config_value: config.inline_config(),
+ config_descriptor: config.descriptor_ptr(),
+ config_descriptor_size: config.descriptor_size(),
+ authority_hash,
+ authority_descriptor: ptr::null(),
+ authority_descriptor_size: 0,
+ mode,
+ hidden,
+ },
+ config_descriptor: PhantomData,
+ }
+ }
+
+ /// Returns a raw pointer to the wrapped `DiceInputValues`.
+ pub fn as_ptr(&self) -> *const DiceInputValues {
+ &self.dice_inputs as *const DiceInputValues
+ }
+}
+
+/// Derives a CDI private key seed from a `cdi_attest` value.
+pub fn derive_cdi_private_key_seed(cdi_attest: &Cdi) -> Result<PrivateKeySeed> {
+ let mut seed = PrivateKeySeed::default();
+ check_result(
+ // SAFETY: The function writes to the buffer within the given bounds, and only reads the
+ // input values. The first argument context is not used in this function.
+ unsafe {
+ DiceDeriveCdiPrivateKeySeed(
+ ptr::null_mut(), // context
+ cdi_attest.as_ptr(),
+ seed.as_mut_ptr(),
+ )
+ },
+ seed.0.len(),
+ )?;
+ Ok(seed)
+}
+
+/// Derives an ID from the given `cdi_public_key` value.
+pub fn derive_cdi_certificate_id(cdi_public_key: &[u8]) -> Result<DiceId> {
+ let mut id = [0u8; ID_SIZE];
+ check_result(
+ // SAFETY: The function writes to the buffer within the given bounds, and only reads the
+ // input values. The first argument context is not used in this function.
+ unsafe {
+ DiceDeriveCdiCertificateId(
+ ptr::null_mut(), // context
+ cdi_public_key.as_ptr(),
+ cdi_public_key.len(),
+ id.as_mut_ptr(),
+ )
+ },
+ id.len(),
+ )?;
+ Ok(id)
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+/// Returns the actual size of the next CDI certificate.
+pub fn dice_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ input_values: &InputValues,
+ next_cdi_certificate: &mut [u8],
+ next_cdi_values: &mut CdiValues,
+) -> Result<usize> {
+ let mut next_cdi_certificate_actual_size = 0;
+ check_result(
+ // SAFETY: The function only reads the current CDI values and inputs and writes
+ // to `next_cdi_certificate` and next CDI values within its bounds.
+ // The first argument can be null and is not used in the current implementation.
+ unsafe {
+ DiceMainFlow(
+ ptr::null_mut(), // context
+ current_cdi_attest.as_ptr(),
+ current_cdi_seal.as_ptr(),
+ input_values.as_ptr(),
+ next_cdi_certificate.len(),
+ next_cdi_certificate.as_mut_ptr(),
+ &mut next_cdi_certificate_actual_size,
+ next_cdi_values.cdi_attest.as_mut_ptr(),
+ next_cdi_values.cdi_seal.as_mut_ptr(),
+ )
+ },
+ next_cdi_certificate_actual_size,
+ )?;
+ Ok(next_cdi_certificate_actual_size)
+}
diff --git a/libs/dice/open_dice/src/error.rs b/libs/dice/open_dice/src/error.rs
new file mode 100644
index 0000000..53ffd2d
--- /dev/null
+++ b/libs/dice/open_dice/src/error.rs
@@ -0,0 +1,63 @@
+// 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.
+
+//! Errors and relating functions thrown in this library.
+
+use open_dice_cbor_bindgen::DiceResult;
+use std::{fmt, result};
+
+#[cfg(feature = "std")]
+use std::error::Error;
+
+/// Error type used by DICE.
+#[derive(Debug)]
+pub enum DiceError {
+ /// Provided input was invalid.
+ InvalidInput,
+ /// Provided buffer was too small.
+ BufferTooSmall(usize),
+ /// Platform error.
+ PlatformError,
+}
+
+/// This makes `DiceError` accepted by anyhow.
+#[cfg(feature = "std")]
+impl Error for DiceError {}
+
+impl fmt::Display for DiceError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::InvalidInput => write!(f, "invalid input"),
+ Self::BufferTooSmall(buffer_required_size) => {
+ write!(f, "buffer too small. Required {buffer_required_size} bytes.")
+ }
+ Self::PlatformError => write!(f, "platform error"),
+ }
+ }
+}
+
+/// DICE result type.
+pub type Result<T> = result::Result<T, DiceError>;
+
+/// Checks the given `DiceResult`. Returns an error if it's not OK.
+pub(crate) fn check_result(result: DiceResult, buffer_required_size: usize) -> Result<()> {
+ match result {
+ DiceResult::kDiceResultOk => Ok(()),
+ DiceResult::kDiceResultInvalidInput => Err(DiceError::InvalidInput),
+ DiceResult::kDiceResultBufferTooSmall => {
+ Err(DiceError::BufferTooSmall(buffer_required_size))
+ }
+ DiceResult::kDiceResultPlatformError => Err(DiceError::PlatformError),
+ }
+}
diff --git a/libs/dice/open_dice/src/lib.rs b/libs/dice/open_dice/src/lib.rs
new file mode 100644
index 0000000..d0004b1
--- /dev/null
+++ b/libs/dice/open_dice/src/lib.rs
@@ -0,0 +1,50 @@
+// 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.
+
+//! Implements safe wrappers around the public API of libopen-dice for
+//! both std and nostd usages.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+
+#[cfg(not(feature = "std"))]
+extern crate core as std;
+
+mod bcc;
+mod dice;
+mod error;
+mod ops;
+#[cfg(feature = "alloc")]
+mod retry;
+
+pub use bcc::{
+ bcc_format_config_descriptor, bcc_handover_main_flow, bcc_handover_parse, bcc_main_flow,
+ BccHandover, DiceConfigValues,
+};
+pub use dice::{
+ derive_cdi_certificate_id, derive_cdi_private_key_seed, dice_main_flow, Cdi, CdiValues, Config,
+ DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, PrivateKey, PrivateKeySeed,
+ PublicKey, Signature, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+pub use error::{DiceError, Result};
+pub use ops::{
+ derive_cdi_leaf_priv, generate_certificate, hash, kdf, keypair_from_seed, sign, verify,
+};
+#[cfg(feature = "alloc")]
+pub use retry::{
+ retry_bcc_format_config_descriptor, retry_bcc_main_flow, retry_dice_main_flow,
+ retry_generate_certificate, OwnedDiceArtifacts,
+};
diff --git a/libs/dice/open_dice/src/ops.rs b/libs/dice/open_dice/src/ops.rs
new file mode 100644
index 0000000..7174d3e
--- /dev/null
+++ b/libs/dice/open_dice/src/ops.rs
@@ -0,0 +1,173 @@
+// 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.
+
+//! This module mirrors the content in open-dice/include/dice/ops.h
+//! It contains the set of functions that implement various operations that the
+//! main DICE functions depend on.
+
+use crate::dice::{
+ derive_cdi_private_key_seed, DiceArtifacts, Hash, InputValues, PrivateKey, PublicKey,
+ Signature, HASH_SIZE, PRIVATE_KEY_SEED_SIZE, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE,
+};
+use crate::error::{check_result, Result};
+use open_dice_cbor_bindgen::{
+ DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
+};
+use std::ptr;
+
+/// Hashes the provided input using DICE's hash function `DiceHash`.
+pub fn hash(input: &[u8]) -> Result<Hash> {
+ let mut output: Hash = [0; HASH_SIZE];
+ check_result(
+ // SAFETY: DiceHash takes a sized input buffer and writes to a constant-sized output
+ // buffer. The first argument context is not used in this function.
+ unsafe {
+ DiceHash(
+ ptr::null_mut(), // context
+ input.as_ptr(),
+ input.len(),
+ output.as_mut_ptr(),
+ )
+ },
+ output.len(),
+ )?;
+ Ok(output)
+}
+
+/// An implementation of HKDF-SHA512. Derives a key of `derived_key.len()` bytes from `ikm`, `salt`,
+/// and `info`. The derived key is written to the `derived_key`.
+pub fn kdf(ikm: &[u8], salt: &[u8], info: &[u8], derived_key: &mut [u8]) -> Result<()> {
+ check_result(
+ // SAFETY: The function writes to the `derived_key`, within the given bounds, and only
+ // reads the input values. The first argument context is not used in this function.
+ unsafe {
+ DiceKdf(
+ ptr::null_mut(), // context
+ derived_key.len(),
+ ikm.as_ptr(),
+ ikm.len(),
+ salt.as_ptr(),
+ salt.len(),
+ info.as_ptr(),
+ info.len(),
+ derived_key.as_mut_ptr(),
+ )
+ },
+ derived_key.len(),
+ )
+}
+
+/// Deterministically generates a public and private key pair from `seed`.
+/// Since this is deterministic, `seed` is as sensitive as a private key and can
+/// be used directly as the private key.
+pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(PublicKey, PrivateKey)> {
+ let mut public_key = [0u8; PUBLIC_KEY_SIZE];
+ let mut private_key = PrivateKey::default();
+ check_result(
+ // SAFETY: The function writes to the `public_key` and `private_key` within the given
+ // bounds, and only reads the `seed`. The first argument context is not used in this
+ // function.
+ unsafe {
+ DiceKeypairFromSeed(
+ ptr::null_mut(), // context
+ seed.as_ptr(),
+ public_key.as_mut_ptr(),
+ private_key.as_mut_ptr(),
+ )
+ },
+ public_key.len(),
+ )?;
+ Ok((public_key, private_key))
+}
+
+/// Derives the CDI_Leaf_Priv from the provided `dice_artifacts`.
+///
+/// The corresponding public key is included in the leaf certificate of the DICE chain
+/// contained in `dice_artifacts`.
+pub fn derive_cdi_leaf_priv(dice_artifacts: &dyn DiceArtifacts) -> Result<PrivateKey> {
+ let cdi_priv_key_seed = derive_cdi_private_key_seed(dice_artifacts.cdi_attest())?;
+ let (_, private_key) = keypair_from_seed(cdi_priv_key_seed.as_array())?;
+ Ok(private_key)
+}
+
+/// Signs the `message` with the give `private_key` using `DiceSign`.
+pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Signature> {
+ let mut signature = [0u8; SIGNATURE_SIZE];
+ check_result(
+ // SAFETY: The function writes to the `signature` within the given bounds, and only reads
+ // the message and the private key. The first argument context is not used in this
+ // function.
+ unsafe {
+ DiceSign(
+ ptr::null_mut(), // context
+ message.as_ptr(),
+ message.len(),
+ private_key.as_ptr(),
+ signature.as_mut_ptr(),
+ )
+ },
+ signature.len(),
+ )?;
+ Ok(signature)
+}
+
+/// Verifies the `signature` of the `message` with the given `public_key` using `DiceVerify`.
+pub fn verify(message: &[u8], signature: &Signature, public_key: &PublicKey) -> Result<()> {
+ check_result(
+ // SAFETY: only reads the messages, signature and public key as constant values.
+ // The first argument context is not used in this function.
+ unsafe {
+ DiceVerify(
+ ptr::null_mut(), // context
+ message.as_ptr(),
+ message.len(),
+ signature.as_ptr(),
+ public_key.as_ptr(),
+ )
+ },
+ 0,
+ )
+}
+
+/// Generates an X.509 certificate from the given `subject_private_key_seed` and
+/// `input_values`, and signed by `authority_private_key_seed`.
+/// The subject private key seed is supplied here so the implementation can choose
+/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
+/// Returns the actual size of the generated certificate.
+pub fn generate_certificate(
+ subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ input_values: &InputValues,
+ certificate: &mut [u8],
+) -> Result<usize> {
+ let mut certificate_actual_size = 0;
+ check_result(
+ // SAFETY: The function writes to the `certificate` within the given bounds, and only reads
+ // the input values and the key seeds. The first argument context is not used in this
+ // function.
+ unsafe {
+ DiceGenerateCertificate(
+ ptr::null_mut(), // context
+ subject_private_key_seed.as_ptr(),
+ authority_private_key_seed.as_ptr(),
+ input_values.as_ptr(),
+ certificate.len(),
+ certificate.as_mut_ptr(),
+ &mut certificate_actual_size,
+ )
+ },
+ certificate_actual_size,
+ )?;
+ Ok(certificate_actual_size)
+}
diff --git a/libs/dice/open_dice/src/retry.rs b/libs/dice/open_dice/src/retry.rs
new file mode 100644
index 0000000..a6303bd
--- /dev/null
+++ b/libs/dice/open_dice/src/retry.rs
@@ -0,0 +1,141 @@
+// 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.
+
+//! This module implements a retry version for multiple DICE functions that
+//! require preallocated output buffer. As the retry functions require
+//! memory allocation on heap, currently we only expose these functions in
+//! std environment.
+
+use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow, DiceConfigValues};
+use crate::dice::{
+ dice_main_flow, Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+use crate::error::{DiceError, Result};
+use crate::ops::generate_certificate;
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+/// Artifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
+/// and the BCC formatted attestation certificate chain.
+/// As we align with the DICE standards today, this is the certificate chain
+/// is also called DICE certificate chain.
+#[derive(Debug)]
+pub struct OwnedDiceArtifacts {
+ /// CDI Values.
+ cdi_values: CdiValues,
+ /// Boot Certificate Chain.
+ bcc: Vec<u8>,
+}
+
+impl DiceArtifacts for OwnedDiceArtifacts {
+ fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
+ &self.cdi_values.cdi_attest
+ }
+
+ fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
+ &self.cdi_values.cdi_seal
+ }
+
+ fn bcc(&self) -> Option<&[u8]> {
+ Some(&self.bcc)
+ }
+}
+
+/// Retries the given function with bigger measured buffer size.
+fn retry_with_measured_buffer<F>(mut f: F) -> Result<Vec<u8>>
+where
+ F: FnMut(&mut Vec<u8>) -> Result<usize>,
+{
+ let mut buffer = Vec::new();
+ match f(&mut buffer) {
+ Err(DiceError::BufferTooSmall(actual_size)) => {
+ buffer.resize(actual_size, 0);
+ f(&mut buffer)?;
+ }
+ Err(e) => return Err(e),
+ Ok(_) => {}
+ };
+ Ok(buffer)
+}
+
+/// Formats a configuration descriptor following the BCC's specification.
+pub fn retry_bcc_format_config_descriptor(values: &DiceConfigValues) -> Result<Vec<u8>> {
+ retry_with_measured_buffer(|buffer| bcc_format_config_descriptor(values, buffer))
+}
+
+/// Executes the main BCC flow.
+///
+/// Given a full set of input values along with the current BCC and CDI values,
+/// computes the next CDI values and matching updated BCC.
+pub fn retry_bcc_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ bcc: &[u8],
+ input_values: &InputValues,
+) -> Result<OwnedDiceArtifacts> {
+ let mut next_cdi_values = CdiValues::default();
+ let next_bcc = retry_with_measured_buffer(|next_bcc| {
+ bcc_main_flow(
+ current_cdi_attest,
+ current_cdi_seal,
+ bcc,
+ input_values,
+ &mut next_cdi_values,
+ next_bcc,
+ )
+ })?;
+ Ok(OwnedDiceArtifacts { cdi_values: next_cdi_values, bcc: next_bcc })
+}
+
+/// Executes the main DICE flow.
+///
+/// Given a full set of input values and the current CDI values, computes the
+/// next CDI values and a matching certificate.
+pub fn retry_dice_main_flow(
+ current_cdi_attest: &Cdi,
+ current_cdi_seal: &Cdi,
+ input_values: &InputValues,
+) -> Result<(CdiValues, Vec<u8>)> {
+ let mut next_cdi_values = CdiValues::default();
+ let next_cdi_certificate = retry_with_measured_buffer(|next_cdi_certificate| {
+ dice_main_flow(
+ current_cdi_attest,
+ current_cdi_seal,
+ input_values,
+ next_cdi_certificate,
+ &mut next_cdi_values,
+ )
+ })?;
+ Ok((next_cdi_values, next_cdi_certificate))
+}
+
+/// Generates an X.509 certificate from the given `subject_private_key_seed` and
+/// `input_values`, and signed by `authority_private_key_seed`.
+/// The subject private key seed is supplied here so the implementation can choose
+/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
+/// Returns the generated certificate.
+pub fn retry_generate_certificate(
+ subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
+ input_values: &InputValues,
+) -> Result<Vec<u8>> {
+ retry_with_measured_buffer(|certificate| {
+ generate_certificate(
+ subject_private_key_seed,
+ authority_private_key_seed,
+ input_values,
+ certificate,
+ )
+ })
+}
diff --git a/libs/dice/open_dice/tests/api_test.rs b/libs/dice/open_dice/tests/api_test.rs
new file mode 100644
index 0000000..a47265b
--- /dev/null
+++ b/libs/dice/open_dice/tests/api_test.rs
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 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.
+ */
+
+use diced_open_dice::{
+ derive_cdi_certificate_id, derive_cdi_private_key_seed, hash, kdf, keypair_from_seed, sign,
+ verify, CDI_SIZE, HASH_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+};
+
+#[test]
+fn hash_succeeds() {
+ const EXPECTED_HASH: [u8; HASH_SIZE] = [
+ 0x30, 0x9e, 0xcc, 0x48, 0x9c, 0x12, 0xd6, 0xeb, 0x4c, 0xc4, 0x0f, 0x50, 0xc9, 0x02, 0xf2,
+ 0xb4, 0xd0, 0xed, 0x77, 0xee, 0x51, 0x1a, 0x7c, 0x7a, 0x9b, 0xcd, 0x3c, 0xa8, 0x6d, 0x4c,
+ 0xd8, 0x6f, 0x98, 0x9d, 0xd3, 0x5b, 0xc5, 0xff, 0x49, 0x96, 0x70, 0xda, 0x34, 0x25, 0x5b,
+ 0x45, 0xb0, 0xcf, 0xd8, 0x30, 0xe8, 0x1f, 0x60, 0x5d, 0xcf, 0x7d, 0xc5, 0x54, 0x2e, 0x93,
+ 0xae, 0x9c, 0xd7, 0x6f,
+ ];
+ assert_eq!(EXPECTED_HASH, hash(b"hello world").expect("hash failed"));
+}
+
+#[test]
+fn kdf_succeeds() {
+ let mut derived_key = [0u8; PRIVATE_KEY_SEED_SIZE];
+ kdf(b"myInitialKeyMaterial", b"mySalt", b"myInfo", &mut derived_key).unwrap();
+ const EXPECTED_DERIVED_KEY: [u8; PRIVATE_KEY_SEED_SIZE] = [
+ 0x91, 0x9b, 0x8d, 0x29, 0xc4, 0x1b, 0x93, 0xd7, 0xeb, 0x09, 0xfa, 0xd7, 0xc9, 0x87, 0xb0,
+ 0xd1, 0xcc, 0x26, 0xef, 0x07, 0x83, 0x42, 0xcf, 0xa3, 0x45, 0x0a, 0x57, 0xe9, 0x19, 0x86,
+ 0xef, 0x48,
+ ];
+ assert_eq!(EXPECTED_DERIVED_KEY, derived_key);
+}
+
+#[test]
+fn derive_cdi_certificate_id_succeeds() {
+ const EXPECTED_ID: [u8; ID_SIZE] = [
+ 0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
+ 0x44, 0x0c, 0xd3, 0xc0, 0x6d,
+ ];
+ assert_eq!(EXPECTED_ID, derive_cdi_certificate_id(b"MyPubKey").unwrap());
+}
+
+const EXPECTED_SEED: &[u8] = &[
+ 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba, 0xaa,
+ 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5, 0x3a, 0x08,
+ 0x84, 0x8a, 0x98, 0x85, 0x6d, 0xf5, 0x69, 0x21, 0x03, 0xcd, 0x09, 0xc3, 0x28, 0xd6, 0x06, 0xa7,
+ 0x57, 0xbd, 0x48, 0x4b, 0x0f, 0x79, 0x0f, 0xf8, 0x2f, 0xf0, 0x0a, 0x41, 0x94, 0xd8, 0x8c, 0xa8,
+];
+
+const EXPECTED_CDI_ATTEST: &[u8] = &[
+ 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba, 0xaa,
+ 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5, 0x3a, 0x08,
+];
+
+const EXPECTED_CDI_PRIVATE_KEY_SEED: &[u8] = &[
+ 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe, 0x0d,
+ 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72, 0x02, 0x6e,
+];
+
+const EXPECTED_PUB_KEY: &[u8] = &[
+ 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23, 0xc9,
+ 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61, 0x06, 0x37,
+];
+const EXPECTED_PRIV_KEY: &[u8] = &[
+ 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe, 0x0d,
+ 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72, 0x02, 0x6e,
+ 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23, 0xc9,
+ 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61, 0x06, 0x37,
+];
+
+const EXPECTED_SIGNATURE: &[u8] = &[
+ 0x44, 0xae, 0xcc, 0xe2, 0xb9, 0x96, 0x18, 0x39, 0x0e, 0x61, 0x0f, 0x53, 0x07, 0xbf, 0xf2, 0x32,
+ 0x3d, 0x44, 0xd4, 0xf2, 0x07, 0x23, 0x30, 0x85, 0x32, 0x18, 0xd2, 0x69, 0xb8, 0x29, 0x3c, 0x26,
+ 0xe6, 0x0d, 0x9c, 0xa5, 0xc2, 0x73, 0xcd, 0x8c, 0xb8, 0x3c, 0x3e, 0x5b, 0xfd, 0x62, 0x8d, 0xf6,
+ 0xc4, 0x27, 0xa6, 0xe9, 0x11, 0x06, 0x5a, 0xb2, 0x2b, 0x64, 0xf7, 0xfc, 0xbb, 0xab, 0x4a, 0x0e,
+];
+
+#[test]
+fn hash_derive_sign_verify() {
+ let seed = hash(b"MySeedString").unwrap();
+ assert_eq!(seed, EXPECTED_SEED);
+ let cdi_attest = &seed[..CDI_SIZE];
+ assert_eq!(cdi_attest, EXPECTED_CDI_ATTEST);
+ let cdi_private_key_seed = derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
+ assert_eq!(cdi_private_key_seed.as_array(), EXPECTED_CDI_PRIVATE_KEY_SEED);
+ let (pub_key, priv_key) = keypair_from_seed(cdi_private_key_seed.as_array()).unwrap();
+ assert_eq!(&pub_key, EXPECTED_PUB_KEY);
+ assert_eq!(priv_key.as_array(), EXPECTED_PRIV_KEY);
+ let mut signature = sign(b"MyMessage", priv_key.as_array()).unwrap();
+ assert_eq!(&signature, EXPECTED_SIGNATURE);
+ assert!(verify(b"MyMessage", &signature, &pub_key).is_ok());
+ assert!(verify(b"MyMessage_fail", &signature, &pub_key).is_err());
+ signature[0] += 1;
+ assert!(verify(b"MyMessage", &signature, &pub_key).is_err());
+}
diff --git a/libs/dice/sample_inputs/Android.bp b/libs/dice/sample_inputs/Android.bp
new file mode 100644
index 0000000..013038c
--- /dev/null
+++ b/libs/dice/sample_inputs/Android.bp
@@ -0,0 +1,81 @@
+// Copyright 2021, 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "libdiced_sample_inputs_defaults",
+ crate_name: "diced_sample_inputs",
+ defaults: ["avf_build_flags_rust"],
+ srcs: ["src/lib.rs"],
+}
+
+rust_library {
+ name: "libdiced_sample_inputs",
+ defaults: ["libdiced_sample_inputs_defaults"],
+ features: [
+ "std",
+ ],
+ rustlibs: [
+ "libciborium",
+ "libcoset",
+ "libdiced_open_dice",
+ "liblog_rust",
+ ],
+}
+
+rust_library_rlib {
+ name: "libdiced_sample_inputs_nostd",
+ defaults: ["libdiced_sample_inputs_defaults"],
+ rustlibs: [
+ "libciborium_nostd",
+ "libcoset_nostd",
+ "libdiced_open_dice_nostd",
+ "liblog_rust_nostd",
+ ],
+ visibility: [
+ "//packages/modules/Virtualization:__subpackages__",
+ ],
+}
+
+rust_defaults {
+ name: "libdiced_sample_inputs_test_defaults",
+ crate_name: "diced_sample_inputs_test",
+ srcs: ["tests/*.rs"],
+ test_suites: ["general-tests"],
+ rustlibs: [
+ "libanyhow",
+ "libhwtrust",
+ ],
+}
+
+rust_test {
+ name: "libdiced_sample_inputs.integration_test",
+ defaults: ["libdiced_sample_inputs_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice",
+ "libdiced_sample_inputs",
+ ],
+}
+
+rust_test {
+ name: "libdiced_sample_inputs_nostd.integration_test",
+ defaults: ["libdiced_sample_inputs_test_defaults"],
+ rustlibs: [
+ "libdiced_open_dice_nostd",
+ "libdiced_sample_inputs_nostd",
+ ],
+}
diff --git a/libs/dice/sample_inputs/src/lib.rs b/libs/dice/sample_inputs/src/lib.rs
new file mode 100644
index 0000000..9d6deca
--- /dev/null
+++ b/libs/dice/sample_inputs/src/lib.rs
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//! Provides a set of sample inputs for a DICE chain and CDI values derived
+//! from it.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+extern crate alloc;
+
+mod sample_inputs;
+
+pub use sample_inputs::make_sample_bcc_and_cdis;
diff --git a/libs/dice/sample_inputs/src/sample_inputs.rs b/libs/dice/sample_inputs/src/sample_inputs.rs
new file mode 100644
index 0000000..54f551b
--- /dev/null
+++ b/libs/dice/sample_inputs/src/sample_inputs.rs
@@ -0,0 +1,200 @@
+// Copyright 2021, 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.
+
+//! This module provides a set of sample input values for a DICE chain, a sample UDS,
+//! as well as tuple of CDIs and BCC derived thereof.
+
+use alloc::vec;
+use alloc::vec::Vec;
+use ciborium::{de, ser, value::Value};
+use core::ffi::CStr;
+use coset::{iana, Algorithm, AsCborValue, CoseKey, KeyOperation, KeyType, Label};
+use diced_open_dice::{
+ derive_cdi_private_key_seed, keypair_from_seed, retry_bcc_format_config_descriptor,
+ retry_bcc_main_flow, retry_dice_main_flow, Config, DiceArtifacts, DiceConfigValues, DiceError,
+ DiceMode, InputValues, OwnedDiceArtifacts, Result, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE,
+};
+use log::error;
+
+/// Sample UDS used to perform the root dice flow by `make_sample_bcc_and_cdis`.
+const UDS: &[u8; CDI_SIZE] = &[
+ 0x65, 0x4f, 0xab, 0xa9, 0xa5, 0xad, 0x0f, 0x5e, 0x15, 0xc3, 0x12, 0xf7, 0x77, 0x45, 0xfa, 0x55,
+ 0x18, 0x6a, 0xa6, 0x34, 0xb6, 0x7c, 0x82, 0x7b, 0x89, 0x4c, 0xc5, 0x52, 0xd3, 0x27, 0x35, 0x8e,
+];
+
+const CODE_HASH_ABL: [u8; HASH_SIZE] = [
+ 0x16, 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26,
+ 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe, 0x25,
+ 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d, 0xd3, 0xfb,
+ 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15, 0x98, 0x14,
+];
+const AUTHORITY_HASH_ABL: [u8; HASH_SIZE] = [
+ 0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb, 0x3c, 0xe7,
+ 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1, 0x23, 0xe6, 0xc8, 0xdf,
+ 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b, 0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd,
+ 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d,
+];
+const HIDDEN_ABL: [u8; HIDDEN_SIZE] = [
+ 0xa2, 0x01, 0xd0, 0xc0, 0xaa, 0x75, 0x3c, 0x06, 0x43, 0x98, 0x6c, 0xc3, 0x5a, 0xb5, 0x5f, 0x1f,
+ 0x0f, 0x92, 0x44, 0x3b, 0x0e, 0xd4, 0x29, 0x75, 0xe3, 0xdb, 0x36, 0xda, 0xc8, 0x07, 0x97, 0x4d,
+ 0xff, 0xbc, 0x6a, 0xa4, 0x8a, 0xef, 0xc4, 0x7f, 0xf8, 0x61, 0x7d, 0x51, 0x4d, 0x2f, 0xdf, 0x7e,
+ 0x8c, 0x3d, 0xa3, 0xfc, 0x63, 0xd4, 0xd4, 0x74, 0x8a, 0xc4, 0x14, 0x45, 0x83, 0x6b, 0x12, 0x7e,
+];
+const CODE_HASH_AVB: [u8; HASH_SIZE] = [
+ 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83, 0x7f, 0x46, 0x8d,
+ 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56, 0xb3, 0xbf, 0x2f, 0xfa,
+ 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18, 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f,
+ 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71, 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7,
+];
+const AUTHORITY_HASH_AVB: [u8; HASH_SIZE] = [
+ 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35, 0x2b, 0xaa,
+ 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43,
+ 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab,
+ 0x3d, 0x0f, 0x89, 0xa4, 0x6f, 0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98,
+];
+const HIDDEN_AVB: [u8; HIDDEN_SIZE] = [
+ 0x5b, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x5e, 0x64, 0xe5, 0x64, 0x3f, 0xfd, 0x21, 0x09,
+ 0x9d, 0xf3, 0xcd, 0xc7, 0xa4, 0x2a, 0xe2, 0x97, 0xdd, 0xe2, 0x4f, 0xb0, 0x7d, 0x7e, 0xf5, 0x8e,
+ 0xd6, 0x4d, 0x84, 0x25, 0x54, 0x41, 0x3f, 0x8f, 0x78, 0x64, 0x1a, 0x51, 0x27, 0x9d, 0x55, 0x8a,
+ 0xe9, 0x90, 0x35, 0xab, 0x39, 0x80, 0x4b, 0x94, 0x40, 0x84, 0xa2, 0xfd, 0x73, 0xeb, 0x35, 0x7a,
+];
+const AUTHORITY_HASH_ANDROID: [u8; HASH_SIZE] = [
+ 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03, 0xb8, 0xd6,
+ 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37, 0x68, 0x4e, 0x1d, 0xc0,
+ 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43, 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77,
+ 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7, 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f,
+];
+
+fn ed25519_public_key_to_cbor_value(public_key: &[u8]) -> Result<Value> {
+ let key = CoseKey {
+ kty: KeyType::Assigned(iana::KeyType::OKP),
+ alg: Some(Algorithm::Assigned(iana::Algorithm::EdDSA)),
+ key_ops: vec![KeyOperation::Assigned(iana::KeyOperation::Verify)].into_iter().collect(),
+ params: vec![
+ (
+ Label::Int(iana::Ec2KeyParameter::Crv as i64),
+ Value::from(iana::EllipticCurve::Ed25519 as u64),
+ ),
+ (Label::Int(iana::Ec2KeyParameter::X as i64), Value::Bytes(public_key.to_vec())),
+ ],
+ ..Default::default()
+ };
+ key.to_cbor_value().map_err(|e| {
+ error!("Failed to serialize the key to CBOR data: {e}");
+ DiceError::InvalidInput
+ })
+}
+
+/// Makes a DICE chain (BCC) from the sample input.
+///
+/// The DICE chain is of the following format:
+/// public key derived from UDS -> ABL certificate -> AVB certificate -> Android certificate
+pub fn make_sample_bcc_and_cdis() -> Result<OwnedDiceArtifacts> {
+ let private_key_seed = derive_cdi_private_key_seed(UDS).map_err(|e| {
+ error!("In make_sample_bcc_and_cdis: Trying to derive private key seed. Error: {e}");
+ e
+ })?;
+
+ // Gets the root public key in DICE chain (BCC).
+ let (public_key, _) = keypair_from_seed(private_key_seed.as_array()).map_err(|e| {
+ error!("In make_sample_bcc_and_cids: Failed to generate key pair. Error: {e}");
+ e
+ })?;
+ let ed25519_public_key_value = ed25519_public_key_to_cbor_value(&public_key)?;
+
+ // Gets the ABL certificate to as the root certificate of DICE chain.
+ let config_values = DiceConfigValues {
+ component_name: Some(CStr::from_bytes_with_nul(b"ABL\0").unwrap()),
+ component_version: Some(1),
+ resettable: true,
+ ..Default::default()
+ };
+ let config_descriptor = retry_bcc_format_config_descriptor(&config_values)?;
+ let input_values = InputValues::new(
+ CODE_HASH_ABL,
+ Config::Descriptor(config_descriptor.as_slice()),
+ AUTHORITY_HASH_ABL,
+ DiceMode::kDiceModeNormal,
+ HIDDEN_ABL,
+ );
+ let (cdi_values, cert) = retry_dice_main_flow(UDS, UDS, &input_values).map_err(|e| {
+ error!("In make_sample_bcc_and_cdis: Trying to run first main flow. Error: {e}");
+ e
+ })?;
+ let bcc_value = Value::Array(vec![
+ ed25519_public_key_value,
+ de::from_reader(&cert[..]).map_err(|e| {
+ error!("Deserialize root DICE certificate failed: {e}");
+ DiceError::InvalidInput
+ })?,
+ ]);
+ let mut bcc: Vec<u8> = vec![];
+ ser::into_writer(&bcc_value, &mut bcc).map_err(|e| {
+ error!("Serialize BCC failed: {e}");
+ DiceError::InvalidInput
+ })?;
+
+ // Appends AVB certificate to DICE chain.
+ let config_values = DiceConfigValues {
+ component_name: Some(CStr::from_bytes_with_nul(b"AVB\0").unwrap()),
+ component_version: Some(1),
+ resettable: true,
+ ..Default::default()
+ };
+ let config_descriptor = retry_bcc_format_config_descriptor(&config_values)?;
+ let input_values = InputValues::new(
+ CODE_HASH_AVB,
+ Config::Descriptor(config_descriptor.as_slice()),
+ AUTHORITY_HASH_AVB,
+ DiceMode::kDiceModeNormal,
+ HIDDEN_AVB,
+ );
+ let dice_artifacts =
+ retry_bcc_main_flow(&cdi_values.cdi_attest, &cdi_values.cdi_seal, &bcc, &input_values)
+ .map_err(|e| {
+ error!(
+ "In make_sample_bcc_and_cdis: Trying to run first bcc main flow. Error: {e}"
+ );
+ e
+ })?;
+
+ // Appends Android certificate to DICE chain.
+ let config_values = DiceConfigValues {
+ component_name: Some(CStr::from_bytes_with_nul(b"Android\0").unwrap()),
+ component_version: Some(12),
+ resettable: true,
+ ..Default::default()
+ };
+ let config_descriptor = retry_bcc_format_config_descriptor(&config_values)?;
+ let input_values = InputValues::new(
+ [0u8; HASH_SIZE], // code_hash
+ Config::Descriptor(config_descriptor.as_slice()),
+ AUTHORITY_HASH_ANDROID,
+ DiceMode::kDiceModeNormal,
+ [0u8; HIDDEN_SIZE], // hidden
+ );
+ retry_bcc_main_flow(
+ dice_artifacts.cdi_attest(),
+ dice_artifacts.cdi_seal(),
+ dice_artifacts.bcc().ok_or_else(|| {
+ error!("bcc is none");
+ DiceError::InvalidInput
+ })?,
+ &input_values,
+ )
+ .map_err(|e| {
+ error!("In make_sample_bcc_and_cdis: Trying to run second bcc main flow. Error: {e}");
+ e
+ })
+}
diff --git a/libs/dice/sample_inputs/tests/api_test.rs b/libs/dice/sample_inputs/tests/api_test.rs
new file mode 100644
index 0000000..0823f16
--- /dev/null
+++ b/libs/dice/sample_inputs/tests/api_test.rs
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 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.
+ */
+
+use anyhow::Result;
+use diced_open_dice::{derive_cdi_leaf_priv, sign, DiceArtifacts};
+use diced_sample_inputs::make_sample_bcc_and_cdis;
+use hwtrust::{dice, session::Session};
+
+const EXPECTED_SAMPLE_CDI_ATTEST: &[u8] = &[
+ 0x3e, 0x57, 0x65, 0x5d, 0x48, 0x02, 0xbd, 0x5c, 0x66, 0xcc, 0x1f, 0x0f, 0xbe, 0x5e, 0x32, 0xb6,
+ 0x9e, 0x3d, 0x04, 0xaf, 0x00, 0x15, 0xbc, 0xdd, 0x1f, 0xbc, 0x59, 0xe4, 0xc3, 0x87, 0x95, 0x5e,
+];
+
+const EXPECTED_SAMPLE_CDI_SEAL: &[u8] = &[
+ 0x36, 0x1b, 0xd2, 0xb3, 0xc4, 0xda, 0x77, 0xb2, 0x9c, 0xba, 0x39, 0x53, 0x82, 0x93, 0xd9, 0xb8,
+ 0x9f, 0x73, 0x2d, 0x27, 0x06, 0x15, 0xa8, 0xcb, 0x6d, 0x1d, 0xf2, 0xb1, 0x54, 0xbb, 0x62, 0xf1,
+];
+
+const EXPECTED_SAMPLE_BCC: &[u8] = &[
+ 0x84, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x3e, 0x85,
+ 0xe5, 0x72, 0x75, 0x55, 0xe5, 0x1e, 0xe7, 0xf3, 0x35, 0x94, 0x8e, 0xbb, 0xbd, 0x74, 0x1e, 0x1d,
+ 0xca, 0x49, 0x9c, 0x97, 0x39, 0x77, 0x06, 0xd3, 0xc8, 0x6e, 0x8b, 0xd7, 0x33, 0xf9, 0x84, 0x43,
+ 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01, 0x78, 0x28, 0x34, 0x32, 0x64, 0x38, 0x38,
+ 0x36, 0x34, 0x66, 0x39, 0x37, 0x62, 0x36, 0x35, 0x34, 0x37, 0x61, 0x35, 0x30, 0x63, 0x31, 0x65,
+ 0x30, 0x61, 0x37, 0x34, 0x39, 0x66, 0x38, 0x65, 0x66, 0x38, 0x62, 0x38, 0x31, 0x65, 0x63, 0x36,
+ 0x32, 0x61, 0x66, 0x02, 0x78, 0x28, 0x31, 0x66, 0x36, 0x39, 0x36, 0x66, 0x30, 0x37, 0x32, 0x35,
+ 0x32, 0x66, 0x32, 0x39, 0x65, 0x39, 0x33, 0x66, 0x65, 0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65,
+ 0x33, 0x32, 0x63, 0x64, 0x38, 0x31, 0x64, 0x63, 0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x3a, 0x00,
+ 0x47, 0x44, 0x50, 0x58, 0x40, 0x16, 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38,
+ 0xc3, 0x64, 0x38, 0x63, 0x26, 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34,
+ 0x4c, 0x6d, 0xa2, 0xbe, 0x25, 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2,
+ 0xb3, 0x91, 0x4d, 0xd3, 0xfb, 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba,
+ 0x30, 0xf7, 0x15, 0x98, 0x14, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56, 0xa3, 0x3a, 0x00, 0x01, 0x11,
+ 0x71, 0x63, 0x41, 0x42, 0x4c, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01, 0x3a, 0x00, 0x01, 0x11, 0x73,
+ 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x47, 0xae, 0x42, 0x27, 0x4c, 0xcb, 0x65, 0x4d,
+ 0xee, 0x74, 0x2d, 0x05, 0x78, 0x2a, 0x08, 0x2a, 0xa5, 0xf0, 0xcf, 0xea, 0x3e, 0x60, 0xee, 0x97,
+ 0x11, 0x4b, 0x5b, 0xe6, 0x05, 0x0c, 0xe8, 0x90, 0xf5, 0x22, 0xc4, 0xc6, 0x67, 0x7a, 0x22, 0x27,
+ 0x17, 0xb3, 0x79, 0xcc, 0x37, 0x64, 0x5e, 0x19, 0x4f, 0x96, 0x37, 0x67, 0x3c, 0xd0, 0xc5, 0xed,
+ 0x0f, 0xdd, 0xe7, 0x2e, 0x4f, 0x70, 0x97, 0x30, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xf9,
+ 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb, 0x3c, 0xe7, 0x6b,
+ 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1, 0x23, 0xe6, 0xc8, 0xdf, 0xfb,
+ 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b, 0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd, 0xc1,
+ 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d, 0x3a,
+ 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01,
+ 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0xb1, 0x02, 0xcc, 0x2c, 0xb2, 0x6a,
+ 0x3b, 0xe9, 0xc1, 0xd3, 0x95, 0x10, 0xa0, 0xe1, 0xff, 0x51, 0xde, 0x57, 0xd5, 0x65, 0x28, 0xfd,
+ 0x7f, 0xeb, 0xd4, 0xca, 0x15, 0xf3, 0xca, 0xdf, 0x37, 0x88, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41,
+ 0x20, 0x58, 0x40, 0x58, 0xd8, 0x03, 0x24, 0x53, 0x60, 0x57, 0xa9, 0x09, 0xfa, 0xab, 0xdc, 0x57,
+ 0x1e, 0xf0, 0xe5, 0x1e, 0x51, 0x6f, 0x9e, 0xa3, 0x42, 0xe6, 0x6a, 0x8c, 0xaa, 0xad, 0x08, 0x48,
+ 0xde, 0x7f, 0x4f, 0x6e, 0x2f, 0x7f, 0x39, 0x6c, 0xa1, 0xf8, 0x42, 0x71, 0xfe, 0x17, 0x3d, 0xca,
+ 0x31, 0x83, 0x92, 0xed, 0xbb, 0x40, 0xb8, 0x10, 0xe0, 0xf2, 0x5a, 0x99, 0x53, 0x38, 0x46, 0x33,
+ 0x97, 0x78, 0x05, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01, 0x78, 0x28,
+ 0x31, 0x66, 0x36, 0x39, 0x36, 0x66, 0x30, 0x37, 0x32, 0x35, 0x32, 0x66, 0x32, 0x39, 0x65, 0x39,
+ 0x33, 0x66, 0x65, 0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65, 0x33, 0x32, 0x63, 0x64, 0x38, 0x31,
+ 0x64, 0x63, 0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x02, 0x78, 0x28, 0x32, 0x35, 0x39, 0x34, 0x38,
+ 0x39, 0x65, 0x36, 0x39, 0x37, 0x34, 0x38, 0x37, 0x30, 0x35, 0x64, 0x65, 0x33, 0x65, 0x32, 0x66,
+ 0x34, 0x34, 0x32, 0x36, 0x37, 0x65, 0x61, 0x34, 0x39, 0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35,
+ 0x37, 0x32, 0x35, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa,
+ 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83, 0x7f, 0x46, 0x8d, 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14,
+ 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56, 0xb3, 0xbf, 0x2f, 0xfa, 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4,
+ 0xd2, 0xa9, 0x6b, 0x18, 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f, 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54,
+ 0xe9, 0x71, 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56,
+ 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x63, 0x41, 0x56, 0x42, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01,
+ 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x93, 0x17, 0xe1,
+ 0x11, 0x27, 0x59, 0xd0, 0xef, 0x75, 0x0b, 0x2b, 0x1c, 0x0f, 0x5f, 0x52, 0xc3, 0x29, 0x23, 0xb5,
+ 0x2a, 0xe6, 0x12, 0x72, 0x6f, 0x39, 0x86, 0x65, 0x2d, 0xf2, 0xe4, 0xe7, 0xd0, 0xaf, 0x0e, 0xa7,
+ 0x99, 0x16, 0x89, 0x97, 0x21, 0xf7, 0xdc, 0x89, 0xdc, 0xde, 0xbb, 0x94, 0x88, 0x1f, 0xda, 0xe2,
+ 0xf3, 0xe0, 0x54, 0xf9, 0x0e, 0x29, 0xb1, 0xbd, 0xe1, 0x0c, 0x0b, 0xd7, 0xf6, 0x3a, 0x00, 0x47,
+ 0x44, 0x54, 0x58, 0x40, 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56, 0xd9,
+ 0x02, 0x35, 0x2b, 0xaa, 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9,
+ 0x35, 0x7d, 0xe4, 0x43, 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e,
+ 0xf6, 0x66, 0xef, 0xab, 0x3d, 0x0f, 0x89, 0xa4, 0x6f, 0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a,
+ 0xef, 0xbc, 0x05, 0x98, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57,
+ 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x96,
+ 0x6d, 0x96, 0x42, 0xda, 0x64, 0x51, 0xad, 0xfa, 0x00, 0xbc, 0xbc, 0x95, 0x8a, 0xb0, 0xb9, 0x76,
+ 0x01, 0xe6, 0xbd, 0xc0, 0x26, 0x79, 0x26, 0xfc, 0x0f, 0x1d, 0x87, 0x65, 0xf1, 0xf3, 0x99, 0x3a,
+ 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0x10, 0x7f, 0x77, 0xad, 0x70, 0xbd, 0x52, 0x81,
+ 0x28, 0x8d, 0x24, 0x81, 0xb4, 0x3f, 0x21, 0x68, 0x9f, 0xc3, 0x80, 0x68, 0x86, 0x55, 0xfb, 0x2e,
+ 0x6d, 0x96, 0xe1, 0xe1, 0xb7, 0x28, 0x8d, 0x63, 0x85, 0xba, 0x2a, 0x01, 0x33, 0x87, 0x60, 0x63,
+ 0xbb, 0x16, 0x3f, 0x2f, 0x3d, 0xf4, 0x2d, 0x48, 0x5b, 0x87, 0xed, 0xda, 0x34, 0xeb, 0x9c, 0x4d,
+ 0x14, 0xac, 0x65, 0xf4, 0xfa, 0xef, 0x45, 0x0b, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01,
+ 0x8f, 0xa9, 0x01, 0x78, 0x28, 0x32, 0x35, 0x39, 0x34, 0x38, 0x39, 0x65, 0x36, 0x39, 0x37, 0x34,
+ 0x38, 0x37, 0x30, 0x35, 0x64, 0x65, 0x33, 0x65, 0x32, 0x66, 0x34, 0x34, 0x32, 0x36, 0x37, 0x65,
+ 0x61, 0x34, 0x39, 0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32, 0x35, 0x02, 0x78, 0x28,
+ 0x35, 0x64, 0x34, 0x65, 0x64, 0x37, 0x66, 0x34, 0x31, 0x37, 0x61, 0x39, 0x35, 0x34, 0x61, 0x31,
+ 0x38, 0x31, 0x34, 0x30, 0x37, 0x62, 0x35, 0x38, 0x38, 0x35, 0x61, 0x66, 0x64, 0x37, 0x32, 0x61,
+ 0x35, 0x62, 0x66, 0x34, 0x30, 0x64, 0x61, 0x36, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
+ 0x00, 0x47, 0x44, 0x53, 0x58, 0x1a, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x67, 0x41, 0x6e, 0x64,
+ 0x72, 0x6f, 0x69, 0x64, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x0c, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6,
+ 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x26, 0x1a, 0xbd, 0x26, 0xd8, 0x37, 0x8f, 0x4a, 0xf2,
+ 0x9e, 0x49, 0x4d, 0x93, 0x23, 0xc4, 0x6e, 0x02, 0xda, 0xe0, 0x00, 0x02, 0xe7, 0xed, 0x29, 0xdf,
+ 0x2b, 0xb3, 0x69, 0xf3, 0x55, 0x0e, 0x4c, 0x22, 0xdc, 0xcf, 0xf5, 0x92, 0xc9, 0xfa, 0x78, 0x98,
+ 0xf1, 0x0e, 0x55, 0x5f, 0xf4, 0x45, 0xed, 0xc0, 0x0a, 0x72, 0x2a, 0x7a, 0x3a, 0xd2, 0xb1, 0xf7,
+ 0x76, 0xfe, 0x2a, 0x6b, 0x7b, 0x2a, 0x53, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0x04, 0x25,
+ 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03, 0xb8, 0xd6, 0xe1, 0x99,
+ 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37, 0x68, 0x4e, 0x1d, 0xc0, 0x24, 0x74,
+ 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43, 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77, 0x0a, 0xde,
+ 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7, 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f, 0x3a, 0x00,
+ 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03,
+ 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0xdb, 0xe7, 0x5b, 0x3f, 0xa3, 0x42, 0xb0,
+ 0x9c, 0xf8, 0x40, 0x8c, 0xb0, 0x9c, 0xf0, 0x0a, 0xaf, 0xdf, 0x6f, 0xe5, 0x09, 0x21, 0x11, 0x92,
+ 0xe1, 0xf8, 0xc5, 0x09, 0x02, 0x3d, 0x1f, 0xb7, 0xc5, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20,
+ 0x58, 0x40, 0xc4, 0xc1, 0xd7, 0x1c, 0x2d, 0x26, 0x89, 0x22, 0xcf, 0xa6, 0x99, 0x77, 0x30, 0x84,
+ 0x86, 0x27, 0x59, 0x8f, 0xd8, 0x08, 0x75, 0xe0, 0xb2, 0xef, 0xf9, 0xfa, 0xa5, 0x40, 0x8c, 0xd3,
+ 0xeb, 0xbb, 0xda, 0xf2, 0xc8, 0xae, 0x41, 0x22, 0x50, 0x9c, 0xe8, 0xb2, 0x9c, 0x9b, 0x3f, 0x8a,
+ 0x78, 0x76, 0xab, 0xd0, 0xbe, 0xfc, 0xe4, 0x79, 0xcb, 0x1b, 0x2b, 0xaa, 0x4d, 0xdd, 0x15, 0x61,
+ 0x42, 0x06,
+];
+const MESSAGE: &[u8] = b"Message for testing";
+
+#[test]
+fn sample_bcc_and_cdis_are_as_expected() {
+ let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
+ assert_eq!(dice_artifacts.cdi_attest(), EXPECTED_SAMPLE_CDI_ATTEST);
+ assert_eq!(dice_artifacts.cdi_seal(), EXPECTED_SAMPLE_CDI_SEAL);
+ assert_eq!(dice_artifacts.bcc(), Some(EXPECTED_SAMPLE_BCC));
+}
+
+#[test]
+fn cdi_leaf_priv_corresponds_to_leaf_public_key_in_dice_chain() -> Result<()> {
+ let dice_artifacts = make_sample_bcc_and_cdis().unwrap();
+ let private_key = derive_cdi_leaf_priv(&dice_artifacts).unwrap();
+ let signature = sign(MESSAGE, private_key.as_array()).unwrap();
+
+ let session = Session::default();
+ let chain = dice::Chain::from_cbor(&session, dice_artifacts.bcc().unwrap())?;
+ let public_key = chain.leaf().subject_public_key();
+ public_key.verify(&signature, MESSAGE)
+}
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index 402040c..0a05471 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -66,28 +66,28 @@
genrule {
name: "fdt_test_tree_one_memory_range_dtb",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["tests/data/test_tree_one_memory_range.dts"],
out: ["data/test_tree_one_memory_range.dtb"],
}
genrule {
name: "fdt_test_tree_multiple_memory_ranges_dtb",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["tests/data/test_tree_multiple_memory_ranges.dts"],
out: ["data/test_tree_multiple_memory_ranges.dtb"],
}
genrule {
name: "fdt_test_tree_empty_memory_range_dtb",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["tests/data/test_tree_empty_memory_range.dts"],
out: ["data/test_tree_empty_memory_range.dtb"],
}
genrule {
name: "fdt_test_tree_no_memory_node_dtb",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["tests/data/test_tree_no_memory_node.dts"],
out: ["data/test_tree_no_memory_node.dtb"],
}
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/dice.rs b/microdroid_manager/src/dice.rs
index 27905c9..a576416 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -1,10 +1,10 @@
-// Copyright 2022, The Android Open Source Project
+// 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
+// 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,
@@ -12,142 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Logic for handling the DICE values and boot operations.
-
-use anyhow::{anyhow, bail, Context, Error, Result};
-use byteorder::{NativeEndian, ReadBytesExt};
+use crate::dice_driver::DiceDriver;
+use crate::{is_debuggable, MicrodroidData};
+use anyhow::{bail, Context, Result};
use ciborium::{cbor, ser};
-use diced_open_dice::{
- bcc_handover_parse, retry_bcc_main_flow, BccHandover, Config, DiceArtifacts, DiceMode, Hash,
- Hidden, InputValues, OwnedDiceArtifacts,
-};
-use keystore2_crypto::ZVec;
-use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
+use diced_open_dice::OwnedDiceArtifacts;
use microdroid_metadata::PayloadMetadata;
-use openssl::hkdf::hkdf;
-use openssl::md::Md;
-use std::fs;
-use std::os::unix::io::AsRawFd;
-use std::path::{Path, PathBuf};
-use std::ptr::null_mut;
-use std::slice;
+use openssl::sha::Sha512;
-/// Artifacts that are mapped into the process address space from the driver.
-pub enum DiceDriver<'a> {
- Real {
- driver_path: PathBuf,
- mmap_addr: *mut c_void,
- mmap_size: usize,
- bcc_handover: BccHandover<'a>,
- },
- Fake(OwnedDiceArtifacts),
-}
-
-impl DiceDriver<'_> {
- fn dice_artifacts(&self) -> &dyn DiceArtifacts {
- match self {
- Self::Real { bcc_handover, .. } => bcc_handover,
- Self::Fake(owned_dice_artifacts) => owned_dice_artifacts,
- }
+/// Perform an open DICE derivation for the payload.
+pub fn dice_derivation(
+ dice: DiceDriver,
+ verified_data: &MicrodroidData,
+ payload_metadata: &PayloadMetadata,
+) -> Result<OwnedDiceArtifacts> {
+ // Calculate compound digests of code and authorities
+ let mut code_hash_ctx = Sha512::new();
+ let mut authority_hash_ctx = Sha512::new();
+ code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
+ authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
+ for extra_apk in &verified_data.extra_apks_data {
+ code_hash_ctx.update(extra_apk.root_hash.as_ref());
+ authority_hash_ctx.update(extra_apk.pubkey.as_ref());
}
-
- pub fn new(driver_path: &Path) -> Result<Self> {
- if driver_path.exists() {
- log::info!("Using DICE values from driver");
- } else if super::is_strict_boot() {
- bail!("Strict boot requires DICE value from driver but none were found");
- } else {
- log::warn!("Using sample DICE values");
- let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()
- .expect("Failed to create sample dice artifacts.");
- return Ok(Self::Fake(dice_artifacts));
- };
-
- let mut file = fs::File::open(driver_path)
- .map_err(|error| Error::new(error).context("Opening driver"))?;
- let mmap_size =
- file.read_u64::<NativeEndian>()
- .map_err(|error| Error::new(error).context("Reading driver"))? as usize;
- // SAFETY: It's safe to map the driver as the service will only create a single
- // mapping per process.
- let mmap_addr = unsafe {
- let fd = file.as_raw_fd();
- mmap(null_mut(), mmap_size, PROT_READ, MAP_PRIVATE, fd, 0)
- };
- if mmap_addr == MAP_FAILED {
- bail!("Failed to mmap {:?}", driver_path);
- }
- let mmap_buf =
- // SAFETY: The slice is created for the region of memory that was just
- // successfully mapped into the process address space so it will be
- // accessible and not referenced from anywhere else.
- unsafe { slice::from_raw_parts((mmap_addr as *const u8).as_ref().unwrap(), mmap_size) };
- let bcc_handover =
- bcc_handover_parse(mmap_buf).map_err(|_| anyhow!("Failed to parse Bcc Handover"))?;
- Ok(Self::Real {
- driver_path: driver_path.to_path_buf(),
- mmap_addr,
- mmap_size,
- bcc_handover,
- })
+ for apex in &verified_data.apex_data {
+ code_hash_ctx.update(apex.root_digest.as_ref());
+ authority_hash_ctx.update(apex.public_key.as_ref());
}
+ let code_hash = code_hash_ctx.finish();
+ let authority_hash = authority_hash_ctx.finish();
- /// Derives a sealing key of `key_length` bytes from the DICE sealing CDI.
- pub fn get_sealing_key(&self, identifier: &[u8], key_length: usize) -> Result<ZVec> {
- // Deterministically derive a key to use for sealing data, rather than using the CDI
- // directly, so we have the chance to rotate the key if needed. A salt isn't needed as the
- // input key material is already cryptographically strong.
- let mut key = ZVec::new(key_length)?;
- let salt = &[];
- hkdf(&mut key, Md::sha256(), self.dice_artifacts().cdi_seal(), salt, identifier)?;
- Ok(key)
- }
+ let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
- pub fn derive(
- self,
- code_hash: Hash,
- config_desc: &[u8],
- authority_hash: Hash,
- debug: bool,
- hidden: Hidden,
- ) -> Result<OwnedDiceArtifacts> {
- let input_values = InputValues::new(
- code_hash,
- Config::Descriptor(config_desc),
- authority_hash,
- if debug { DiceMode::kDiceModeDebug } else { DiceMode::kDiceModeNormal },
- hidden,
- );
- let current_dice_artifacts = self.dice_artifacts();
- let next_dice_artifacts = retry_bcc_main_flow(
- current_dice_artifacts.cdi_attest(),
- current_dice_artifacts.cdi_seal(),
- current_dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
- &input_values,
- )
- .context("DICE derive from driver")?;
- if let Self::Real { driver_path, .. } = &self {
- // Writing to the device wipes the artifacts. The string is ignored by the driver but
- // included for documentation.
- fs::write(driver_path, "wipe")
- .map_err(|err| Error::new(err).context("Wiping driver"))?;
- }
- Ok(next_dice_artifacts)
- }
-}
+ // Check debuggability, conservatively assuming it is debuggable
+ let debuggable = is_debuggable()?;
-impl Drop for DiceDriver<'_> {
- fn drop(&mut self) {
- if let &mut Self::Real { mmap_addr, mmap_size, .. } = self {
- // SAFETY: All references to the mapped region have the same lifetime as self. Since
- // self is being dropped, so are all the references to the mapped region meaning it's
- // safe to unmap.
- let ret = unsafe { munmap(mmap_addr, mmap_size) };
- if ret != 0 {
- log::warn!("Failed to munmap ({})", ret);
- }
- }
- }
+ // Send the details to diced
+ let hidden = verified_data.salt.clone().try_into().unwrap();
+ dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
}
/// Returns a configuration descriptor of the given payload following the BCC's specification:
@@ -160,7 +62,7 @@
/// PayloadConfig = {
/// 1: tstr ; payload_binary_name
/// }
-pub fn format_payload_config_descriptor(payload: &PayloadMetadata) -> Result<Vec<u8>> {
+fn format_payload_config_descriptor(payload: &PayloadMetadata) -> Result<Vec<u8>> {
const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
let config_descriptor_cbor_value = match payload {
diff --git a/microdroid_manager/src/dice_driver.rs b/microdroid_manager/src/dice_driver.rs
new file mode 100644
index 0000000..229f3e0
--- /dev/null
+++ b/microdroid_manager/src/dice_driver.rs
@@ -0,0 +1,149 @@
+// Copyright 2022, 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.
+
+//! Logic for handling the DICE values and boot operations.
+
+use anyhow::{anyhow, bail, Context, Error, Result};
+use byteorder::{NativeEndian, ReadBytesExt};
+use diced_open_dice::{
+ bcc_handover_parse, retry_bcc_main_flow, BccHandover, Config, DiceArtifacts, DiceMode, Hash,
+ Hidden, InputValues, OwnedDiceArtifacts,
+};
+use keystore2_crypto::ZVec;
+use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
+use openssl::hkdf::hkdf;
+use openssl::md::Md;
+use std::fs;
+use std::os::unix::io::AsRawFd;
+use std::path::{Path, PathBuf};
+use std::ptr::null_mut;
+use std::slice;
+
+/// Artifacts that are mapped into the process address space from the driver.
+pub enum DiceDriver<'a> {
+ Real {
+ driver_path: PathBuf,
+ mmap_addr: *mut c_void,
+ mmap_size: usize,
+ bcc_handover: BccHandover<'a>,
+ },
+ Fake(OwnedDiceArtifacts),
+}
+
+impl DiceDriver<'_> {
+ fn dice_artifacts(&self) -> &dyn DiceArtifacts {
+ match self {
+ Self::Real { bcc_handover, .. } => bcc_handover,
+ Self::Fake(owned_dice_artifacts) => owned_dice_artifacts,
+ }
+ }
+
+ pub fn new(driver_path: &Path) -> Result<Self> {
+ if driver_path.exists() {
+ log::info!("Using DICE values from driver");
+ } else if super::is_strict_boot() {
+ bail!("Strict boot requires DICE value from driver but none were found");
+ } else {
+ log::warn!("Using sample DICE values");
+ let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()
+ .expect("Failed to create sample dice artifacts.");
+ return Ok(Self::Fake(dice_artifacts));
+ };
+
+ let mut file = fs::File::open(driver_path)
+ .map_err(|error| Error::new(error).context("Opening driver"))?;
+ let mmap_size =
+ file.read_u64::<NativeEndian>()
+ .map_err(|error| Error::new(error).context("Reading driver"))? as usize;
+ // SAFETY: It's safe to map the driver as the service will only create a single
+ // mapping per process.
+ let mmap_addr = unsafe {
+ let fd = file.as_raw_fd();
+ mmap(null_mut(), mmap_size, PROT_READ, MAP_PRIVATE, fd, 0)
+ };
+ if mmap_addr == MAP_FAILED {
+ bail!("Failed to mmap {:?}", driver_path);
+ }
+ let mmap_buf =
+ // SAFETY: The slice is created for the region of memory that was just
+ // successfully mapped into the process address space so it will be
+ // accessible and not referenced from anywhere else.
+ unsafe { slice::from_raw_parts((mmap_addr as *const u8).as_ref().unwrap(), mmap_size) };
+ let bcc_handover =
+ bcc_handover_parse(mmap_buf).map_err(|_| anyhow!("Failed to parse Bcc Handover"))?;
+ Ok(Self::Real {
+ driver_path: driver_path.to_path_buf(),
+ mmap_addr,
+ mmap_size,
+ bcc_handover,
+ })
+ }
+
+ /// Derives a sealing key of `key_length` bytes from the DICE sealing CDI.
+ pub fn get_sealing_key(&self, identifier: &[u8], key_length: usize) -> Result<ZVec> {
+ // Deterministically derive a key to use for sealing data, rather than using the CDI
+ // directly, so we have the chance to rotate the key if needed. A salt isn't needed as the
+ // input key material is already cryptographically strong.
+ let mut key = ZVec::new(key_length)?;
+ let salt = &[];
+ hkdf(&mut key, Md::sha256(), self.dice_artifacts().cdi_seal(), salt, identifier)?;
+ Ok(key)
+ }
+
+ pub fn derive(
+ self,
+ code_hash: Hash,
+ config_desc: &[u8],
+ authority_hash: Hash,
+ debug: bool,
+ hidden: Hidden,
+ ) -> Result<OwnedDiceArtifacts> {
+ let input_values = InputValues::new(
+ code_hash,
+ Config::Descriptor(config_desc),
+ authority_hash,
+ if debug { DiceMode::kDiceModeDebug } else { DiceMode::kDiceModeNormal },
+ hidden,
+ );
+ let current_dice_artifacts = self.dice_artifacts();
+ let next_dice_artifacts = retry_bcc_main_flow(
+ current_dice_artifacts.cdi_attest(),
+ current_dice_artifacts.cdi_seal(),
+ current_dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
+ &input_values,
+ )
+ .context("DICE derive from driver")?;
+ if let Self::Real { driver_path, .. } = &self {
+ // Writing to the device wipes the artifacts. The string is ignored by the driver but
+ // included for documentation.
+ fs::write(driver_path, "wipe")
+ .map_err(|err| Error::new(err).context("Wiping driver"))?;
+ }
+ Ok(next_dice_artifacts)
+ }
+}
+
+impl Drop for DiceDriver<'_> {
+ fn drop(&mut self) {
+ if let &mut Self::Real { mmap_addr, mmap_size, .. } = self {
+ // SAFETY: All references to the mapped region have the same lifetime as self. Since
+ // self is being dropped, so are all the references to the mapped region meaning it's
+ // safe to unmap.
+ let ret = unsafe { munmap(mmap_addr, mmap_size) };
+ if ret != 0 {
+ log::warn!("Failed to munmap ({})", ret);
+ }
+ }
+ }
+}
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index b16a1e1..2ff04f1 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -33,7 +33,7 @@
//! The payload of a partition is encrypted/signed by a key that is unique to the loader and to the
//! VM as well. Failing to decrypt/authenticate a partition by a loader stops the boot process.
-use crate::dice::DiceDriver;
+use crate::dice_driver::DiceDriver;
use crate::ioutil;
use anyhow::{anyhow, bail, Context, Result};
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index dd0ddbb..8175753 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -15,16 +15,15 @@
//! Microdroid Manager
mod dice;
+mod dice_driver;
mod instance;
mod ioutil;
mod payload;
mod swap;
+mod verify;
mod vm_payload_service;
mod vm_secret;
-use crate::dice::{DiceDriver, format_payload_config_descriptor};
-use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
-use crate::vm_payload_service::register_vm_payload_service;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
@@ -32,54 +31,46 @@
VM_PAYLOAD_SERVICE_SOCKET_NAME,
ENCRYPTEDSTORE_MOUNTPOINT,
};
+
+use crate::dice::dice_derivation;
+use crate::dice_driver::DiceDriver;
+use crate::instance::{ApexData, InstanceDisk, MicrodroidData};
+use crate::verify::verify_payload;
+use crate::vm_payload_service::register_vm_payload_service;
use anyhow::{anyhow, bail, ensure, Context, Error, Result};
-use apkverify::{get_public_key_der, verify, V4Signature};
use binder::Strong;
-use diced_open_dice::OwnedDiceArtifacts;
-use glob::glob;
-use itertools::sorted;
+use keystore2_crypto::ZVec;
use libc::VMADDR_CID_HOST;
use log::{error, info};
-use keystore2_crypto::ZVec;
-use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
+use microdroid_metadata::{write_metadata, PayloadMetadata};
use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
use nix::sys::signal::Signal;
-use openssl::sha::Sha512;
-use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
-use rand::Fill;
+use payload::{load_metadata, to_metadata};
use rpcbinder::RpcSession;
use rustutils::sockets::android_get_control_socket;
use rustutils::system_properties;
use rustutils::system_properties::PropertyWatcher;
use std::borrow::Cow::{Borrowed, Owned};
-use std::convert::TryInto;
use std::env;
use std::ffi::CString;
-use std::fs::{self, create_dir, OpenOptions, File};
+use std::fs::{self, create_dir, File, OpenOptions};
use std::io::{Read, Write};
+use std::os::unix::io::{FromRawFd, OwnedFd};
use std::os::unix::process::CommandExt;
use std::os::unix::process::ExitStatusExt;
-use std::os::unix::io::{FromRawFd, OwnedFd};
use std::path::Path;
use std::process::{Child, Command, Stdio};
use std::str;
-use std::time::{Duration, SystemTime};
+use std::time::Duration;
use vm_secret::VmSecret;
const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
-const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
-const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
-const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
-const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
-const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
-const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
const AVF_DEBUG_POLICY_RAMDUMP: &str = "/sys/firmware/devicetree/base/avf/guest/common/ramdump";
const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
"/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
-const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
const ENCRYPTEDSTORE_BIN: &str = "/system/bin/encryptedstore";
const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
@@ -163,7 +154,7 @@
fn main() -> Result<()> {
// If debuggable, print full backtrace to console log with stdio_to_kmsg
- if system_properties::read_bool(DEBUGGABLE_PROP, true)? {
+ if is_debuggable()? {
env::set_var("RUST_BACKTRACE", "full");
}
@@ -278,36 +269,6 @@
}
Ok(())
}
-fn dice_derivation(
- dice: DiceDriver,
- verified_data: &MicrodroidData,
- payload_metadata: &PayloadMetadata,
-) -> Result<OwnedDiceArtifacts> {
- // Calculate compound digests of code and authorities
- let mut code_hash_ctx = Sha512::new();
- let mut authority_hash_ctx = Sha512::new();
- code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
- authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
- for extra_apk in &verified_data.extra_apks_data {
- code_hash_ctx.update(extra_apk.root_hash.as_ref());
- authority_hash_ctx.update(extra_apk.pubkey.as_ref());
- }
- for apex in &verified_data.apex_data {
- code_hash_ctx.update(apex.root_digest.as_ref());
- authority_hash_ctx.update(apex.public_key.as_ref());
- }
- let code_hash = code_hash_ctx.finish();
- let authority_hash = authority_hash_ctx.finish();
-
- let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
-
- // Check debuggability, conservatively assuming it is debuggable
- let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
-
- // Send the details to diced
- let hidden = verified_data.salt.clone().try_into().unwrap();
- dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
-}
fn is_strict_boot() -> bool {
Path::new(AVF_STRICT_BOOT).exists()
@@ -321,10 +282,14 @@
!Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
}
+fn is_debuggable() -> Result<bool> {
+ Ok(system_properties::read_bool(DEBUGGABLE_PROP, true)?)
+}
+
fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
match config.export_tombstones {
Some(b) => b,
- None => system_properties::read_bool(DEBUGGABLE_PROP, true).unwrap_or(false),
+ None => is_debuggable().unwrap_or(false),
}
}
@@ -424,7 +389,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(verify::DM_MOUNTED_APK_PATH),
Path::new(VM_APK_CONTENTS_PATH),
"microdroid_manager.apk.mounted".to_owned(),
)?;
@@ -490,28 +455,6 @@
exec_task(task, service).context("Failed to run payload")
}
-struct ApkDmverityArgument<'a> {
- apk: &'a str,
- idsig: &'a str,
- name: &'a str,
- saved_root_hash: Option<&'a RootHash>,
-}
-
-fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
- let mut cmd = Command::new(APKDMVERITY_BIN);
-
- for argument in args {
- cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
- if let Some(root_hash) = argument.saved_root_hash {
- cmd.arg(&to_hex_string(root_hash));
- } else {
- cmd.arg("none");
- }
- }
-
- cmd.spawn().context("Spawn apkdmverity")
-}
-
enum MountForExec {
Allowed,
Disallowed,
@@ -582,147 +525,6 @@
Ok(())
}
-// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
-// when the root_hash values from the idsig file and the instance disk are different. This function
-// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
-// saved to the instance disk.
-fn verify_payload(
- metadata: &Metadata,
- saved_data: Option<&MicrodroidData>,
-) -> Result<MicrodroidData> {
- let start_time = SystemTime::now();
-
- // Verify main APK
- let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
- let root_hash_trustful =
- saved_data.map(|d| d.apk_data.root_hash_eq(root_hash_from_idsig.as_ref())).unwrap_or(false);
-
- // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
- // instead of the value read from the idsig file.
- let main_apk_argument = {
- ApkDmverityArgument {
- apk: MAIN_APK_PATH,
- idsig: MAIN_APK_IDSIG_PATH,
- name: MAIN_APK_DEVICE_NAME,
- saved_root_hash: if root_hash_trustful {
- Some(root_hash_from_idsig.as_ref())
- } else {
- None
- },
- }
- };
- let mut apkdmverity_arguments = vec![main_apk_argument];
-
- // Verify extra APKs
- // For now, we can't read the payload config, so glob APKs and idsigs.
- // Later, we'll see if it matches with the payload config.
-
- // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
- // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
- let extra_apks =
- sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
- let extra_idsigs =
- sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
- ensure!(
- extra_apks.len() == extra_idsigs.len(),
- "Extra apks/idsigs mismatch: {} apks but {} idsigs",
- extra_apks.len(),
- extra_idsigs.len()
- );
-
- let extra_root_hashes_from_idsig: Vec<_> = extra_idsigs
- .iter()
- .map(|idsig| {
- get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from extra idsig")
- })
- .collect();
-
- let extra_root_hashes_trustful: Vec<_> = if let Some(data) = saved_data {
- extra_root_hashes_from_idsig
- .iter()
- .enumerate()
- .map(|(i, root_hash)| data.extra_apk_root_hash_eq(i, root_hash))
- .collect()
- } else {
- vec![false; extra_root_hashes_from_idsig.len()]
- };
- let extra_apk_names: Vec<_> =
- (0..extra_apks.len()).map(|i| format!("extra-apk-{}", i)).collect();
-
- for (i, extra_apk) in extra_apks.iter().enumerate() {
- apkdmverity_arguments.push({
- ApkDmverityArgument {
- apk: extra_apk.to_str().unwrap(),
- idsig: extra_idsigs[i].to_str().unwrap(),
- name: &extra_apk_names[i],
- saved_root_hash: if extra_root_hashes_trustful[i] {
- Some(&extra_root_hashes_from_idsig[i])
- } else {
- None
- },
- }
- });
- }
-
- // Start apkdmverity and wait for the dm-verify block
- let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
-
- // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
- // APEX payload.
- let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
-
- // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
- // Skip writing it if the debug policy ignoring identity is on
- if is_verified_boot() {
- write_apex_payload_data(saved_data, &apex_data_from_payload)?;
- }
-
- // Start apexd to activate APEXes
- system_properties::write("ctl.start", "apexd-vm")?;
-
- // TODO(inseob): add timeout
- apkdmverity_child.wait()?;
-
- // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
- // the APK file and therefore can be very slow if the APK is large. Note that this step is
- // taken only when the root_hash is un-trustful which can be either when this is the first boot
- // of the VM or APK was updated in the host.
- // TODO(jooyung): consider multithreading to make this faster
- let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
- let extra_apks_data = extra_root_hashes_from_idsig
- .into_iter()
- .enumerate()
- .map(|(i, extra_root_hash)| {
- let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
- let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
- Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
- })
- .collect::<Result<Vec<_>>>()?;
-
- info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
-
- // Use the salt from a verified instance, or generate a salt for a new instance.
- let salt = if let Some(saved_data) = saved_data {
- saved_data.salt.clone()
- } else if is_strict_boot() {
- // No need to add more entropy as a previous stage must have used a new, random salt.
- vec![0u8; 64]
- } else {
- let mut salt = vec![0u8; 64];
- salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
- salt
- };
-
- // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
- // fully verifying the APK or by comparing it with the saved root_hash.
- Ok(MicrodroidData {
- salt,
- apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
- extra_apks_data,
- apex_data: apex_data_from_payload,
- })
-}
-
fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
// For now, only the number of apks is important, as the mount point and dm-verity name is fixed
for i in 0..config.extra_apks.len() {
@@ -770,28 +572,6 @@
Ok(())
}
-fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
- Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
-}
-
-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 {
- verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
- "failed to verify {}",
- apk
- )))
- } else {
- get_public_key_der(apk, current_sdk)
- }
-}
-
-fn get_current_sdk() -> Result<u32> {
- let current_sdk = system_properties::read("ro.build.version.sdk")?;
- let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
- current_sdk.parse().context("Malformed SDK version")
-}
-
fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
match payload_metadata {
PayloadMetadata::ConfigPath(path) => {
@@ -831,7 +611,7 @@
return Ok(());
}
- let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
+ let debuggable = is_debuggable()?;
let ramdump = get_debug_policy_bool(AVF_DEBUG_POLICY_RAMDUMP)?.unwrap_or_default();
let requested = debuggable | ramdump;
@@ -908,10 +688,6 @@
Ok(path)
}
-fn to_hex_string(buf: &[u8]) -> String {
- buf.iter().map(|b| format!("{:02X}", b)).collect()
-}
-
fn prepare_encryptedstore(vm_secret: &VmSecret) -> Result<Child> {
let mut key = ZVec::new(ENCRYPTEDSTORE_KEYSIZE)?;
vm_secret.derive_encryptedstore_key(&mut key)?;
diff --git a/microdroid_manager/src/verify.rs b/microdroid_manager/src/verify.rs
new file mode 100644
index 0000000..06b15f7
--- /dev/null
+++ b/microdroid_manager/src/verify.rs
@@ -0,0 +1,236 @@
+// 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.
+
+use crate::instance::{ApkData, MicrodroidData, RootHash};
+use crate::payload::get_apex_data_from_payload;
+use crate::{is_strict_boot, is_verified_boot, write_apex_payload_data, MicrodroidError};
+use anyhow::{anyhow, ensure, Context, Result};
+use apkmanifest::get_manifest_info;
+use apkverify::{get_public_key_der, verify, V4Signature};
+use glob::glob;
+use itertools::sorted;
+use log::{info, warn};
+use microdroid_metadata::Metadata;
+use rand::Fill;
+use rustutils::system_properties;
+use std::path::Path;
+use std::process::{Child, Command};
+use std::str;
+use std::time::SystemTime;
+
+pub const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
+
+const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
+const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
+const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
+const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
+const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
+
+const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
+
+/// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
+/// when the root_hash values from the idsig file and the instance disk are different. This function
+/// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
+/// saved to the instance disk.
+pub fn verify_payload(
+ metadata: &Metadata,
+ saved_data: Option<&MicrodroidData>,
+) -> Result<MicrodroidData> {
+ let start_time = SystemTime::now();
+
+ // Verify main APK
+ let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
+ let root_hash_trustful =
+ saved_data.map(|d| d.apk_data.root_hash_eq(root_hash_from_idsig.as_ref())).unwrap_or(false);
+
+ // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
+ // instead of the value read from the idsig file.
+ let main_apk_argument = {
+ ApkDmverityArgument {
+ apk: MAIN_APK_PATH,
+ idsig: MAIN_APK_IDSIG_PATH,
+ name: MAIN_APK_DEVICE_NAME,
+ saved_root_hash: if root_hash_trustful {
+ Some(root_hash_from_idsig.as_ref())
+ } else {
+ None
+ },
+ }
+ };
+ let mut apkdmverity_arguments = vec![main_apk_argument];
+
+ // Verify extra APKs
+ // For now, we can't read the payload config, so glob APKs and idsigs.
+ // Later, we'll see if it matches with the payload config.
+
+ // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
+ // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
+ let extra_apks =
+ sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
+ let extra_idsigs =
+ sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
+ ensure!(
+ extra_apks.len() == extra_idsigs.len(),
+ "Extra apks/idsigs mismatch: {} apks but {} idsigs",
+ extra_apks.len(),
+ extra_idsigs.len()
+ );
+
+ let extra_root_hashes_from_idsig: Vec<_> = extra_idsigs
+ .iter()
+ .map(|idsig| {
+ get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from extra idsig")
+ })
+ .collect();
+
+ let extra_root_hashes_trustful: Vec<_> = if let Some(data) = saved_data {
+ extra_root_hashes_from_idsig
+ .iter()
+ .enumerate()
+ .map(|(i, root_hash)| data.extra_apk_root_hash_eq(i, root_hash))
+ .collect()
+ } else {
+ vec![false; extra_root_hashes_from_idsig.len()]
+ };
+ let extra_apk_names: Vec<_> =
+ (0..extra_apks.len()).map(|i| format!("extra-apk-{}", i)).collect();
+
+ for (i, extra_apk) in extra_apks.iter().enumerate() {
+ apkdmverity_arguments.push({
+ ApkDmverityArgument {
+ apk: extra_apk.to_str().unwrap(),
+ idsig: extra_idsigs[i].to_str().unwrap(),
+ name: &extra_apk_names[i],
+ saved_root_hash: if extra_root_hashes_trustful[i] {
+ Some(&extra_root_hashes_from_idsig[i])
+ } else {
+ None
+ },
+ }
+ });
+ }
+
+ // Start apkdmverity and wait for the dm-verify block
+ let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
+
+ // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
+ // APEX payload.
+ let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
+
+ // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
+ // Skip writing it if the debug policy ignoring identity is on
+ if is_verified_boot() {
+ write_apex_payload_data(saved_data, &apex_data_from_payload)?;
+ }
+
+ // Start apexd to activate APEXes
+ system_properties::write("ctl.start", "apexd-vm")?;
+
+ // TODO(inseob): add timeout
+ apkdmverity_child.wait()?;
+
+ // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
+ // the APK file and therefore can be very slow if the APK is large. Note that this step is
+ // taken only when the root_hash is un-trustful which can be either when this is the first boot
+ // of the VM or APK was updated in the host.
+ // TODO(jooyung): consider multithreading to make this faster
+ let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
+ let extra_apks_data = extra_root_hashes_from_idsig
+ .into_iter()
+ .enumerate()
+ .map(|(i, extra_root_hash)| {
+ let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
+ let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
+ Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
+ })
+ .collect::<Result<Vec<_>>>()?;
+
+ info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
+
+ // Use the salt from a verified instance, or generate a salt for a new instance.
+ let salt = if let Some(saved_data) = saved_data {
+ saved_data.salt.clone()
+ } else if is_strict_boot() {
+ // No need to add more entropy as a previous stage must have used a new, random salt.
+ vec![0u8; 64]
+ } else {
+ let mut salt = vec![0u8; 64];
+ salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
+ salt
+ };
+
+ // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
+ // fully verifying the APK or by comparing it with the saved root_hash.
+ Ok(MicrodroidData {
+ salt,
+ apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
+ extra_apks_data,
+ apex_data: apex_data_from_payload,
+ })
+}
+
+fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
+ Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
+}
+
+fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
+ let current_sdk = get_current_sdk()?;
+
+ 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)?
+ };
+
+ 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> {
+ let current_sdk = system_properties::read("ro.build.version.sdk")?;
+ let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
+ current_sdk.parse().context("Malformed SDK version")
+}
+
+struct ApkDmverityArgument<'a> {
+ apk: &'a str,
+ idsig: &'a str,
+ name: &'a str,
+ saved_root_hash: Option<&'a RootHash>,
+}
+
+fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
+ let mut cmd = Command::new(APKDMVERITY_BIN);
+
+ for argument in args {
+ cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
+ if let Some(root_hash) = argument.saved_root_hash {
+ cmd.arg(&hex::encode(root_hash));
+ } else {
+ cmd.arg("none");
+ }
+ }
+
+ cmd.spawn().context("Spawn apkdmverity")
+}
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index 47a2492..78b6323 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -23,11 +23,11 @@
use log::{info, warn};
use static_assertions::const_assert_eq;
use vmbase::util::RangeExt;
-use zerocopy::{FromBytes, LayoutVerified};
+use zerocopy::{FromBytes, FromZeroes, LayoutVerified};
/// Configuration data header.
#[repr(C, packed)]
-#[derive(Clone, Copy, Debug, FromBytes)]
+#[derive(Clone, Copy, Debug, FromZeroes, FromBytes)]
struct Header {
/// Magic number; must be `Header::MAGIC`.
magic: u32,
@@ -132,14 +132,14 @@
}
#[repr(packed)]
-#[derive(Clone, Copy, Debug, FromBytes)]
+#[derive(Clone, Copy, Debug, FromZeroes, FromBytes)]
struct HeaderEntry {
offset: u32,
size: u32,
}
#[repr(C, packed)]
-#[derive(Clone, Copy, Debug, Eq, FromBytes, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, FromZeroes, FromBytes, PartialEq)]
pub struct Version {
minor: u16,
major: u16,
diff --git a/pvmfw/src/gpt.rs b/pvmfw/src/gpt.rs
index 06bf994..71eb569 100644
--- a/pvmfw/src/gpt.rs
+++ b/pvmfw/src/gpt.rs
@@ -26,6 +26,7 @@
use vmbase::util::ceiling_div;
use vmbase::virtio::{pci, HalImpl};
use zerocopy::FromBytes;
+use zerocopy::FromZeroes;
type VirtIOBlk = pci::VirtIOBlk<HalImpl>;
@@ -156,7 +157,7 @@
type Lba = u64;
/// Structure as defined in release 2.10 of the UEFI Specification (5.3.2 GPT Header).
-#[derive(FromBytes)]
+#[derive(FromZeroes, FromBytes)]
#[repr(C, packed)]
struct Header {
signature: u64,
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
index 22839cb..f2cd6a3 100644
--- a/pvmfw/src/instance.rs
+++ b/pvmfw/src/instance.rs
@@ -35,6 +35,7 @@
use vmbase::virtio::HalImpl;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
+use zerocopy::FromZeroes;
pub enum Error {
/// Unexpected I/O error while accessing the underlying disk.
@@ -174,7 +175,7 @@
}
}
-#[derive(FromBytes)]
+#[derive(FromZeroes, FromBytes)]
#[repr(C, packed)]
struct Header {
magic: [u8; Header::MAGIC.len()],
@@ -258,7 +259,7 @@
/// Marks the start of an instance.img entry.
///
/// Note: Virtualization/microdroid_manager/src/instance.rs uses the name "partition".
-#[derive(AsBytes, FromBytes)]
+#[derive(AsBytes, FromZeroes, FromBytes)]
#[repr(C, packed)]
struct EntryHeader {
uuid: u128,
@@ -279,7 +280,7 @@
}
}
-#[derive(AsBytes, FromBytes)]
+#[derive(AsBytes, FromZeroes, FromBytes)]
#[repr(C)]
struct EntryBody {
code_hash: Hash,
diff --git a/rialto/src/communication.rs b/rialto/src/communication.rs
index 50722f2..1b94912 100644
--- a/rialto/src/communication.rs
+++ b/rialto/src/communication.rs
@@ -100,7 +100,16 @@
}
fn recv(&mut self, buffer: &mut [u8]) -> virtio_drivers::Result<usize> {
- self.connection_manager.recv(self.peer_addr, self.peer_addr.port, buffer)
+ let bytes_read =
+ self.connection_manager.recv(self.peer_addr, self.peer_addr.port, buffer)?;
+
+ let buffer_available_bytes = self
+ .connection_manager
+ .recv_buffer_available_bytes(self.peer_addr, self.peer_addr.port)?;
+ if buffer_available_bytes == 0 && bytes_read > 0 {
+ self.connection_manager.update_credit(self.peer_addr, self.peer_addr.port)?;
+ }
+ Ok(bytes_read)
}
fn wait_for_send(&mut self, buffer: &[u8]) -> virtio_drivers::Result {
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 6a6dcf4..e13b7a1 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -57,9 +57,7 @@
}
fn check_processing_reverse_request(vm: &mut ServiceVm) -> Result<()> {
- // TODO(b/292080257): Test with message longer than the receiver's buffer capacity
- // 1024 bytes once the guest virtio-vsock driver fixes the credit update in recv().
- let message = "abc".repeat(166);
+ let message = "abc".repeat(500);
let request = Request::Reverse(message.as_bytes().to_vec());
let response = vm.process_request(request)?;
@@ -77,8 +75,9 @@
info!("Received response: {response:?}.");
match response {
- Response::GenerateEcdsaP256KeyPair(EcdsaP256KeyPair { maced_public_key, .. }) => {
- assert_array_has_nonzero(&maced_public_key[..]);
+ Response::GenerateEcdsaP256KeyPair(EcdsaP256KeyPair { maced_public_key, key_blob }) => {
+ assert_array_has_nonzero(&maced_public_key);
+ assert_array_has_nonzero(&key_blob);
Ok(maced_public_key)
}
_ => bail!("Incorrect response type: {response:?}"),
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/Android.bp b/service_vm/requests/Android.bp
index 4b9b46f..f85064a 100644
--- a/service_vm/requests/Android.bp
+++ b/service_vm/requests/Android.bp
@@ -3,7 +3,7 @@
}
rust_defaults {
- name: "libservice_vm_requests_defaults",
+ name: "libservice_vm_requests_nostd_defaults",
crate_name: "service_vm_requests",
defaults: ["avf_build_flags_rust"],
srcs: ["src/lib.rs"],
@@ -11,11 +11,6 @@
apex_available: [
"com.android.virt",
],
-}
-
-rust_library_rlib {
- name: "libservice_vm_requests_nostd",
- defaults: ["libservice_vm_requests_defaults"],
no_stdlibs: true,
stdlibs: [
"libcore.rust_sysroot",
@@ -32,3 +27,14 @@
"libzeroize_nostd",
],
}
+
+rust_library_rlib {
+ name: "libservice_vm_requests_nostd",
+ defaults: ["libservice_vm_requests_nostd_defaults"],
+}
+
+rust_test {
+ name: "libservice_vm_requests.test",
+ defaults: ["libservice_vm_requests_nostd_defaults"],
+ test_suites: ["general-tests"],
+}
diff --git a/service_vm/requests/TEST_MAPPING b/service_vm/requests/TEST_MAPPING
new file mode 100644
index 0000000..c95f9e3
--- /dev/null
+++ b/service_vm/requests/TEST_MAPPING
@@ -0,0 +1,9 @@
+// When adding or removing tests here, don't forget to amend _all_modules list in
+// wireless/android/busytown/ath_config/configs/prod/avf/tests.gcl
+{
+ "avf-presubmit" : [
+ {
+ "name" : "libservice_vm_requests.test"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/service_vm/requests/src/cbor.rs b/service_vm/requests/src/cbor.rs
new file mode 100644
index 0000000..36492e5
--- /dev/null
+++ b/service_vm/requests/src/cbor.rs
@@ -0,0 +1,37 @@
+// 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.
+
+//! Utility functions for CBOR serialization/deserialization.
+
+use alloc::vec::Vec;
+use coset::{CoseError, Result};
+use serde::{de::DeserializeOwned, Serialize};
+
+/// Serializes the given data to a CBOR-encoded byte vector.
+pub(crate) fn serialize<T: ?Sized + Serialize>(v: &T) -> Result<Vec<u8>> {
+ let mut data = Vec::new();
+ ciborium::into_writer(v, &mut data)?;
+ Ok(data)
+}
+
+/// Deserializes the given type from a CBOR-encoded byte slice, failing if any extra
+/// data remains after the type has been read.
+pub(crate) fn deserialize<T: DeserializeOwned>(mut data: &[u8]) -> Result<T> {
+ let res = ciborium::from_reader(&mut data)?;
+ if data.is_empty() {
+ Ok(res)
+ } else {
+ Err(CoseError::ExtraneousData)
+ }
+}
diff --git a/service_vm/requests/src/keyblob.rs b/service_vm/requests/src/keyblob.rs
new file mode 100644
index 0000000..a714edd
--- /dev/null
+++ b/service_vm/requests/src/keyblob.rs
@@ -0,0 +1,158 @@
+// 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.
+
+//! Handles the encryption and decryption of the key blob.
+
+use crate::cbor;
+use alloc::vec;
+use alloc::vec::Vec;
+use bssl_avf::{hkdf, rand_bytes, Aead, AeadContext, Digester, AES_GCM_NONCE_LENGTH};
+use core::result;
+use serde::{Deserialize, Serialize};
+use service_vm_comm::RequestProcessingError;
+// TODO(b/241428146): This will be used once the retrieval mechanism is available.
+#[cfg(test)]
+use zeroize::Zeroizing;
+
+type Result<T> = result::Result<T, RequestProcessingError>;
+
+/// The KEK (Key Encryption Key) info is used as information to derive the KEK using HKDF.
+const KEK_INFO: &[u8] = b"rialto keyblob kek";
+
+/// An all-zero nonce is utilized to encrypt the private key. This is because each key
+/// undergoes encryption using a distinct KEK, which is derived from a secret and a random
+/// salt. Since the uniqueness of the IV/key combination is already guaranteed by the uniqueness
+/// of the KEK, there is no need for an additional random nonce.
+const PRIVATE_KEY_NONCE: &[u8; AES_GCM_NONCE_LENGTH] = &[0; AES_GCM_NONCE_LENGTH];
+
+/// Since Rialto functions as both the sender and receiver of the message, no additional data is
+/// needed.
+const PRIVATE_KEY_AD: &[u8] = &[];
+
+// Encrypted key blob.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub(crate) enum EncryptedKeyBlob {
+ /// Version 1 key blob.
+ V1(EncryptedKeyBlobV1),
+}
+
+/// Encrypted key blob version 1.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub(crate) struct EncryptedKeyBlobV1 {
+ /// Salt used to derive the KEK.
+ kek_salt: [u8; 32],
+
+ /// Private key encrypted with AES-256-GCM.
+ encrypted_private_key: Vec<u8>,
+}
+
+impl EncryptedKeyBlob {
+ pub(crate) fn new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self> {
+ EncryptedKeyBlobV1::new(private_key, kek_secret).map(Self::V1)
+ }
+
+ // TODO(b/241428146): Use this function to decrypt the retrieved keyblob once the retrieval
+ // mechanism is available.
+ #[cfg(test)]
+ pub(crate) fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
+ match self {
+ Self::V1(blob) => blob.decrypt_private_key(kek_secret),
+ }
+ }
+
+ // TODO(b/241428146): This function will be used once the retrieval mechanism is available.
+ #[cfg(test)]
+ pub(crate) fn from_cbor_slice(slice: &[u8]) -> coset::Result<Self> {
+ cbor::deserialize(slice)
+ }
+
+ pub(crate) fn to_cbor_vec(&self) -> coset::Result<Vec<u8>> {
+ cbor::serialize(&self)
+ }
+}
+
+impl EncryptedKeyBlobV1 {
+ fn new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self> {
+ let mut kek_salt = [0u8; 32];
+ rand_bytes(&mut kek_salt)?;
+ let kek = hkdf::<32>(kek_secret, &kek_salt, KEK_INFO, Digester::sha512())?;
+
+ let tag_len = None;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
+ let mut out = vec![0u8; private_key.len() + aead_ctx.aead().max_overhead()];
+ let ciphertext = aead_ctx.seal(private_key, PRIVATE_KEY_NONCE, PRIVATE_KEY_AD, &mut out)?;
+
+ Ok(Self { kek_salt, encrypted_private_key: ciphertext.to_vec() })
+ }
+
+ #[cfg(test)]
+ fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
+ let kek = hkdf::<32>(kek_secret, &self.kek_salt, KEK_INFO, Digester::sha512())?;
+ let mut out = Zeroizing::new(vec![0u8; self.encrypted_private_key.len()]);
+ let tag_len = None;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
+ let plaintext = aead_ctx.open(
+ &self.encrypted_private_key,
+ PRIVATE_KEY_NONCE,
+ PRIVATE_KEY_AD,
+ &mut out,
+ )?;
+ Ok(Zeroizing::new(plaintext.to_vec()))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use bssl_avf::{ApiName, CipherError, Error};
+
+ /// The test data are generated randomly with /dev/urandom.
+ const TEST_KEY: [u8; 32] = [
+ 0x76, 0xf7, 0xd5, 0x36, 0x1f, 0x78, 0x58, 0x2e, 0x55, 0x2f, 0x88, 0x9d, 0xa3, 0x3e, 0xba,
+ 0xfb, 0xc1, 0x2b, 0x17, 0x85, 0x24, 0xdc, 0x0e, 0xc4, 0xbf, 0x6d, 0x2e, 0xe8, 0xa8, 0x36,
+ 0x93, 0x62,
+ ];
+ const TEST_SECRET1: [u8; 32] = [
+ 0xac, 0xb1, 0x6b, 0xdf, 0x45, 0x30, 0x20, 0xa5, 0x60, 0x6d, 0x81, 0x07, 0x30, 0x68, 0x6e,
+ 0x01, 0x3d, 0x5e, 0x86, 0xd6, 0xc6, 0x17, 0xfa, 0xd6, 0xe0, 0xff, 0xd4, 0xf0, 0xb0, 0x7c,
+ 0x5c, 0x8f,
+ ];
+ const TEST_SECRET2: [u8; 32] = [
+ 0x04, 0x6e, 0xca, 0x30, 0x5e, 0x6c, 0x8f, 0xe5, 0x1a, 0x47, 0x12, 0xbc, 0x45, 0xd7, 0xa8,
+ 0x38, 0xfb, 0x06, 0xc6, 0x44, 0xa1, 0x21, 0x40, 0x0b, 0x48, 0x88, 0xe2, 0x31, 0x64, 0x42,
+ 0x9d, 0x1c,
+ ];
+
+ #[test]
+ fn decrypting_keyblob_succeeds_with_the_same_kek() -> Result<()> {
+ let encrypted_key_blob = EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?.to_cbor_vec()?;
+ let encrypted_key_blob = EncryptedKeyBlob::from_cbor_slice(&encrypted_key_blob)?;
+ let decrypted_key = encrypted_key_blob.decrypt_private_key(&TEST_SECRET1)?;
+
+ assert_eq!(TEST_KEY, decrypted_key.as_slice());
+ Ok(())
+ }
+
+ #[test]
+ fn decrypting_keyblob_fails_with_a_different_kek() -> Result<()> {
+ let encrypted_key_blob = EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?.to_cbor_vec()?;
+ let encrypted_key_blob = EncryptedKeyBlob::from_cbor_slice(&encrypted_key_blob)?;
+ let err = encrypted_key_blob.decrypt_private_key(&TEST_SECRET2).unwrap_err();
+
+ let expected_err: RequestProcessingError =
+ Error::CallFailed(ApiName::EVP_AEAD_CTX_open, CipherError::BadDecrypt.into()).into();
+ assert_eq!(expected_err, err);
+ Ok(())
+ }
+}
diff --git a/service_vm/requests/src/lib.rs b/service_vm/requests/src/lib.rs
index fc0c87d..6fa6e0b 100644
--- a/service_vm/requests/src/lib.rs
+++ b/service_vm/requests/src/lib.rs
@@ -19,6 +19,8 @@
extern crate alloc;
mod api;
+mod cbor;
+mod keyblob;
mod pub_key;
mod rkp;
diff --git a/service_vm/requests/src/rkp.rs b/service_vm/requests/src/rkp.rs
index f96b85d..933737c 100644
--- a/service_vm/requests/src/rkp.rs
+++ b/service_vm/requests/src/rkp.rs
@@ -15,7 +15,9 @@
//! This module contains functions related to the attestation of the
//! service VM via the RKP (Remote Key Provisioning) server.
-use super::pub_key::{build_maced_public_key, validate_public_key};
+use crate::cbor;
+use crate::keyblob::EncryptedKeyBlob;
+use crate::pub_key::{build_maced_public_key, validate_public_key};
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
@@ -23,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;
@@ -36,7 +38,7 @@
0x82, 0x80, 0xFA, 0xD3, 0xA8, 0x0A, 0x9A, 0x4B, 0xF7, 0xA5, 0x7D, 0x7B, 0xE9, 0xC3, 0xAB, 0x13,
0x89, 0xDC, 0x7B, 0x46, 0xEE, 0x71, 0x22, 0xB4, 0x5F, 0x4C, 0x3F, 0xE2, 0x40, 0x04, 0x3B, 0x6C,
];
-const HMAC_KEY_INFO: &[u8] = b"rialto hmac key";
+const HMAC_KEY_INFO: &[u8] = b"rialto hmac wkey";
const HMAC_KEY_LENGTH: usize = 32;
pub(super) fn generate_ecdsa_p256_key_pair(
@@ -44,13 +46,12 @@
) -> Result<EcdsaP256KeyPair> {
let hmac_key = derive_hmac_key(dice_artifacts)?;
let ec_key = EcKey::new_p256()?;
+
let maced_public_key = build_maced_public_key(ec_key.cose_public_key()?, hmac_key.as_ref())?;
+ let key_blob =
+ EncryptedKeyBlob::new(ec_key.private_key()?.as_slice(), dice_artifacts.cdi_seal())?;
- // TODO(b/279425980): Encrypt the private key in a key blob.
- // Remove the printing of the private key.
- log::debug!("Private key: {:?}", ec_key.private_key()?.as_slice());
-
- let key_pair = EcdsaP256KeyPair { maced_public_key, key_blob: Vec::new() };
+ let key_pair = EcdsaP256KeyPair { maced_public_key, key_blob: key_blob.to_cbor_vec()? };
Ok(key_pair)
}
@@ -80,7 +81,7 @@
// TODO(b/299256925): Add device info in CBOR format here.
Value::Array(public_keys),
])?;
- let csr_payload = cbor_to_vec(&csr_payload)?;
+ let csr_payload = cbor::serialize(&csr_payload)?;
// Builds `SignedData`.
let signed_data_payload =
@@ -91,17 +92,15 @@
// Currently `UdsCerts` is left empty because it is only needed for Samsung devices.
// Check http://b/301574013#comment3 for more information.
let uds_certs = Value::Map(Vec::new());
- let dice_cert_chain = dice_artifacts
- .bcc()
- .map(read_to_value)
- .ok_or(RequestProcessingError::MissingDiceChain)??;
+ let dice_cert_chain = dice_artifacts.bcc().ok_or(RequestProcessingError::MissingDiceChain)?;
+ let dice_cert_chain: Value = cbor::deserialize(dice_cert_chain)?;
let auth_req = cbor!([
Value::Integer(AUTH_REQ_SCHEMA_V1.into()),
uds_certs,
dice_cert_chain,
signed_data,
])?;
- cbor_to_vec(&auth_req)
+ Ok(cbor::serialize(&auth_req)?)
}
fn derive_hmac_key(dice_artifacts: &dyn DiceArtifacts) -> Result<Zeroizing<[u8; HMAC_KEY_LENGTH]>> {
@@ -123,17 +122,12 @@
let protected = HeaderBuilder::new().algorithm(signing_algorithm).build();
let signed_data = CoseSign1Builder::new()
.protected(protected)
- .payload(cbor_to_vec(payload)?)
+ .payload(cbor::serialize(payload)?)
.try_create_signature(&[], |message| sign_message(message, &cdi_leaf_priv))?
.build();
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| {
@@ -142,24 +136,3 @@
})?
.to_vec())
}
-
-fn cbor_to_vec(v: &Value) -> Result<Vec<u8>> {
- let mut data = Vec::new();
- ciborium::into_writer(v, &mut data).map_err(coset::CoseError::from)?;
- Ok(data)
-}
-
-/// Read a CBOR `Value` from a byte slice, failing if any extra data remains
-/// after the `Value` has been read.
-fn read_to_value(mut data: &[u8]) -> Result<Value> {
- let value = ciborium::from_reader(&mut data).map_err(|e| {
- error!("Failed to deserialize the data into CBOR value: {e}");
- RequestProcessingError::CborValueError
- })?;
- if data.is_empty() {
- Ok(value)
- } else {
- error!("CBOR input has extra data.");
- Err(RequestProcessingError::CborValueError)
- }
-}
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index 80fdff7..657241c 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -14,7 +14,7 @@
"androidx.test.runner",
"androidx.test.ext.junit",
"com.android.microdroid.testservice-java",
- "truth-prebuilt",
+ "truth",
],
jni_libs: [
"MicrodroidBenchmarkNativeLib",
diff --git a/tests/helper/Android.bp b/tests/helper/Android.bp
index 6f07efd..614c70c 100644
--- a/tests/helper/Android.bp
+++ b/tests/helper/Android.bp
@@ -17,7 +17,7 @@
"androidx.test.ext.junit",
"com.android.microdroid.testservice-java",
"MicrodroidTestHelper",
- "truth-prebuilt",
+ "truth",
],
sdk_version: "test_current",
}
diff --git a/tests/hostside/helper/Android.bp b/tests/hostside/helper/Android.bp
index e8b6f36..75553d0 100644
--- a/tests/hostside/helper/Android.bp
+++ b/tests/hostside/helper/Android.bp
@@ -9,7 +9,7 @@
"androidx.annotation_annotation",
"compatibility-tradefed",
"tradefed",
- "truth-prebuilt",
+ "truth",
],
static_libs: [
"MicrodroidTestHelper",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 8e12114..692b1b8 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -190,7 +190,7 @@
long start = System.currentTimeMillis();
while ((System.currentTimeMillis() - start < timeoutMillis)
&& !matcher.matches(callable.call())) {
- Thread.sleep(500);
+ RunUtil.getDefault().sleep(500);
}
assertThat(callable.call(), matcher);
}
diff --git a/tests/no_avf/Android.bp b/tests/no_avf/Android.bp
index fd0d5e2..22d099e 100644
--- a/tests/no_avf/Android.bp
+++ b/tests/no_avf/Android.bp
@@ -13,9 +13,9 @@
"androidx.test.runner",
"androidx.test.ext.junit",
"compatibility-common-util-devicesidelib",
- "truth-prebuilt",
+ "truth",
],
sdk_version: "test_current",
compile_multilib: "both",
- min_sdk_version: "UpsideDownCake",
+ min_sdk_version: "34",
}
diff --git a/tests/pvmfw/Android.bp b/tests/pvmfw/Android.bp
index 474c62e..c12f67a 100644
--- a/tests/pvmfw/Android.bp
+++ b/tests/pvmfw/Android.bp
@@ -2,36 +2,30 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-genrule_defaults {
- name: "test_avf_dts_to_dtb",
- tools: ["dtc"],
- cmd: "$(location dtc) -I dts -O dtb $(in) -o $(out)",
-}
-
genrule {
name: "test_avf_debug_policy_with_ramdump",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["assets/avf_debug_policy_with_ramdump.dts"],
out: ["avf_debug_policy_with_ramdump.dtbo"],
}
genrule {
name: "test_avf_debug_policy_without_ramdump",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["assets/avf_debug_policy_without_ramdump.dts"],
out: ["avf_debug_policy_without_ramdump.dtbo"],
}
genrule {
name: "test_avf_debug_policy_with_adb",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["assets/avf_debug_policy_with_adb.dts"],
out: ["avf_debug_policy_with_adb.dtbo"],
}
genrule {
name: "test_avf_debug_policy_without_adb",
- defaults: ["test_avf_dts_to_dtb"],
+ defaults: ["dts_to_dtb"],
srcs: ["assets/avf_debug_policy_without_adb.dts"],
out: ["avf_debug_policy_without_adb.dtbo"],
}
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 526f240..6e8216f 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -41,7 +41,7 @@
"androidx.test.ext.junit",
"authfs_test_apk_assets",
"cbor-java",
- "truth-prebuilt",
+ "truth",
"compatibility-common-util-devicesidelib",
"measure_io_as_jar",
],
diff --git a/tests/vmshareapp/Android.bp b/tests/vmshareapp/Android.bp
index 6c2c9e4..5f6dc57 100644
--- a/tests/vmshareapp/Android.bp
+++ b/tests/vmshareapp/Android.bp
@@ -12,5 +12,5 @@
// Defined in ../testapk/Android.bp
"MicrodroidPayloadInOtherAppNativeLib",
],
- min_sdk_version: "UpsideDownCake",
+ min_sdk_version: "34",
}
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 684aa64..5283ffe 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -474,7 +474,7 @@
.into_iter()
.map(|x| VfioDevice {
sysfs_path: PathBuf::from(&x.sysfsPath),
- dtbo_node: x.dtboNode,
+ dtbo_label: x.dtboLabel,
})
.collect::<Vec<_>>()
} else {
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index b053d99..bb6066f 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -128,7 +128,7 @@
#[derive(Clone, Debug)]
pub struct VfioDevice {
pub sysfs_path: PathBuf,
- pub dtbo_node: String,
+ pub dtbo_label: String,
}
/// The lifecycle state which the payload in the VM has reported itself to be in.
@@ -716,7 +716,7 @@
}
if let Some(p) = path.to_str() {
- Ok(format!("--vfio={p},iommu=viommu,dt-symbol={0}", device.dtbo_node))
+ Ok(format!("--vfio={p},iommu=viommu,dt-symbol={0}", device.dtbo_label))
} else {
bail!("invalid path {path:?}");
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index f3a7617..172dc59 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -25,7 +25,7 @@
interface IVirtualizationServiceInternal {
parcelable BoundDevice {
String sysfsPath;
- String dtboNode;
+ String dtboLabel;
}
/**
* Removes the memlock rlimit of the calling process.
diff --git a/virtualizationservice/assignable_devices.xsd b/virtualizationservice/assignable_devices.xsd
index 8f43019..2fbc1c9 100644
--- a/virtualizationservice/assignable_devices.xsd
+++ b/virtualizationservice/assignable_devices.xsd
@@ -25,7 +25,7 @@
</xs:element>
<xs:complexType name="device">
<xs:attribute name="kind" type="xs:string"/>
- <xs:attribute name="dtbo_node" type="xs:string"/>
+ <xs:attribute name="dtbo_label" type="xs:string"/>
<xs:attribute name="sysfs_path" type="xs:string"/>
</xs:complexType>
</xs:schema>
diff --git a/virtualizationservice/schema/current.txt b/virtualizationservice/schema/current.txt
index ef99294..6e3fbb6 100644
--- a/virtualizationservice/schema/current.txt
+++ b/virtualizationservice/schema/current.txt
@@ -3,10 +3,10 @@
public class Device {
ctor public Device();
- method public String getDtbo_node();
+ method public String getDtbo_label();
method public String getKind();
method public String getSysfs_path();
- method public void setDtbo_node(String);
+ method public void setDtbo_label(String);
method public void setKind(String);
method public void setSysfs_path(String);
}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index ed5c513..a19ecd2 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -209,7 +209,7 @@
.into_iter()
.filter_map(|x| {
if devices.contains(&x.sysfs_path) {
- Some(BoundDevice { sysfsPath: x.sysfs_path, dtboNode: x.dtbo_node })
+ Some(BoundDevice { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
} else {
None
}
@@ -222,7 +222,7 @@
#[derive(Debug, Deserialize)]
struct Device {
kind: String,
- dtbo_node: String,
+ dtbo_label: String,
sysfs_path: String,
}
diff --git a/virtualizationservice/vfio_handler/src/aidl.rs b/virtualizationservice/vfio_handler/src/aidl.rs
index 1c3c5d9..2968ff9 100644
--- a/virtualizationservice/vfio_handler/src/aidl.rs
+++ b/virtualizationservice/vfio_handler/src/aidl.rs
@@ -26,6 +26,7 @@
use rustutils::system_properties;
use zerocopy::{
byteorder::{BigEndian, U32},
+ FromZeroes,
FromBytes,
};
@@ -82,7 +83,7 @@
/// The structure of DT table header in dtbo.img.
/// https://source.android.com/docs/core/architecture/dto/partitions
#[repr(C)]
-#[derive(Debug, FromBytes)]
+#[derive(Debug, FromZeroes, FromBytes)]
struct DtTableHeader {
/// DT_TABLE_MAGIC
magic: U32<BigEndian>,
@@ -106,7 +107,7 @@
/// The structure of each DT table entry (v0) in dtbo.img.
/// https://source.android.com/docs/core/architecture/dto/partitions
#[repr(C)]
-#[derive(Debug, FromBytes)]
+#[derive(Debug, FromZeroes, FromBytes)]
struct DtTableEntry {
/// size of each DT
dt_size: U32<BigEndian>,
@@ -238,7 +239,7 @@
if dt_table_header.magic.get() != DT_TABLE_MAGIC
|| dt_table_header.header_size.get() as usize != size_of::<DtTableHeader>()
{
- return Err(anyhow!("DtTableHeader is invalid")).or_service_specific_exception(-1)?;
+ return Err(anyhow!("DtTableHeader is invalid")).or_service_specific_exception(-1);
}
Ok(dt_table_header)
}
@@ -249,7 +250,7 @@
index: u32,
) -> binder::Result<DtTableEntry> {
if index >= header.dt_entry_count.get() {
- return Err(anyhow!("Invalid dtbo index {index}")).or_service_specific_exception(-1)?;
+ return Err(anyhow!("Invalid dtbo index {index}")).or_service_specific_exception(-1);
}
let Some(prev_dt_entry_total_size) = header.dt_entry_size.get().checked_mul(index) else {
return Err(anyhow!("Unexpected arithmetic result"))