Merge "Filter-out android.vbmeta.device when reading bootconfig"
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 353b597..471b0cf 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -14,6 +14,7 @@
"libandroid_logger",
"libanyhow",
"libauthfs_crypto_bindgen",
+ "libauthfs_fsverity_metadata",
"libbinder_rpc_unstable_bindgen",
"libbinder_rs",
"libcfg_if",
@@ -69,15 +70,12 @@
srcs: [
"testdata/cert.der",
"testdata/input.4k",
- "testdata/input.4k.fsv_sig",
- "testdata/input.4k.merkle_dump",
+ "testdata/input.4k.fsv_meta",
"testdata/input.4k1",
- "testdata/input.4k1.fsv_sig",
- "testdata/input.4k1.merkle_dump",
+ "testdata/input.4k1.fsv_meta",
"testdata/input.4m",
- "testdata/input.4m.fsv_sig",
- "testdata/input.4m.merkle_dump",
- "testdata/input.4m.merkle_dump.bad",
+ "testdata/input.4m.fsv_meta",
+ "testdata/input.4m.fsv_meta.bad_merkle",
],
}
diff --git a/authfs/fd_server/Android.bp b/authfs/fd_server/Android.bp
index 327df0d..9499cd2 100644
--- a/authfs/fd_server/Android.bp
+++ b/authfs/fd_server/Android.bp
@@ -9,6 +9,7 @@
"authfs_aidl_interface-rust",
"libandroid_logger",
"libanyhow",
+ "libauthfs_fsverity_metadata",
"libbinder_common",
"libbinder_rpc_unstable_bindgen",
"libbinder_rs",
diff --git a/authfs/fd_server/src/aidl.rs b/authfs/fd_server/src/aidl.rs
index c2206c8..125b991 100644
--- a/authfs/fd_server/src/aidl.rs
+++ b/authfs/fd_server/src/aidl.rs
@@ -38,6 +38,9 @@
use authfs_aidl_interface::binder::{
BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
};
+use authfs_fsverity_metadata::{
+ get_fsverity_metadata_path, parse_fsverity_metadata, FSVerityMetadata,
+};
use binder_common::{new_binder_exception, new_binder_service_specific_error};
/// Bitflags of forbidden file mode, e.g. setuid, setgid and sticky bit.
@@ -51,13 +54,8 @@
/// The file to read from. fs-verity metadata can be retrieved from this file's FD.
file: File,
- /// Alternative Merkle tree stored in another file.
- /// TODO(205987437): Replace with .fsv_meta file.
- alt_merkle_tree: Option<File>,
-
- /// Alternative signature stored in another file.
- /// TODO(205987437): Replace with .fsv_meta file.
- alt_signature: Option<File>,
+ // Alternative metadata storing merkle tree and signature.
+ alt_metadata: Option<Box<FSVerityMetadata>>,
},
/// A readable/writable file to serve by this server. This backing file should just be a
@@ -139,23 +137,23 @@
let offset: u64 = validate_and_cast_offset(offset)?;
self.handle_fd(id, |config| match config {
- FdConfig::Readonly { file, alt_merkle_tree, .. } => {
- if let Some(tree_file) = &alt_merkle_tree {
- read_into_buf(tree_file, size, offset).map_err(|e| {
+ FdConfig::Readonly { file, alt_metadata, .. } => {
+ let mut buf = vec![0; size];
+
+ let s = if let Some(metadata) = &alt_metadata {
+ metadata.read_merkle_tree(offset, &mut buf).map_err(|e| {
error!("readFsverityMerkleTree: read error: {}", e);
new_errno_error(Errno::EIO)
- })
+ })?
} else {
- let mut buf = vec![0; size];
- let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf)
- .map_err(|e| {
- error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
- new_errno_error(Errno::EIO)
- })?;
- debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
- buf.truncate(s);
- Ok(buf)
- }
+ fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf).map_err(|e| {
+ error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
+ new_errno_error(Errno::EIO)
+ })?
+ };
+ debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
+ buf.truncate(s);
+ Ok(buf)
}
FdConfig::ReadWrite(_file) => {
// For a writable file, Merkle tree is not expected to be served since Auth FS
@@ -169,15 +167,16 @@
fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
self.handle_fd(id, |config| match config {
- FdConfig::Readonly { file, alt_signature, .. } => {
- if let Some(sig_file) = &alt_signature {
- // Supposedly big enough buffer size to store signature.
- let size = MAX_REQUESTING_DATA as usize;
- let offset = 0;
- read_into_buf(sig_file, size, offset).map_err(|e| {
- error!("readFsveritySignature: read error: {}", e);
- new_errno_error(Errno::EIO)
- })
+ FdConfig::Readonly { file, alt_metadata, .. } => {
+ if let Some(metadata) = &alt_metadata {
+ if let Some(signature) = &metadata.signature {
+ Ok(signature.clone())
+ } else {
+ Err(new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ "metadata doesn't contain a signature".to_string(),
+ ))
+ }
} else {
let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
@@ -267,11 +266,12 @@
FdConfig::InputDir(dir) => {
let file = open_readonly_at(dir.as_raw_fd(), &path_buf).map_err(new_errno_error)?;
- // TODO(205987437): Provide the corresponding ".fsv_meta" file when it's created.
- Ok((
- file.as_raw_fd(),
- FdConfig::Readonly { file, alt_merkle_tree: None, alt_signature: None },
- ))
+ let metadata_path_buf = get_fsverity_metadata_path(&path_buf);
+ let metadata = open_readonly_at(dir.as_raw_fd(), &metadata_path_buf)
+ .ok()
+ .and_then(|f| parse_fsverity_metadata(f).ok());
+
+ Ok((file.as_raw_fd(), FdConfig::Readonly { file, alt_metadata: metadata }))
}
FdConfig::OutputDir(_) => {
Err(new_errno_error(Errno::ENOSYS)) // TODO: Implement when needed
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index f17b7e8..fe0475f 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -34,6 +34,7 @@
use std::os::unix::io::FromRawFd;
use aidl::{FdConfig, FdService};
+use authfs_fsverity_metadata::parse_fsverity_metadata;
const RPC_SERVICE_PORT: u32 = 3264; // TODO: support dynamic port for multiple fd_server instances
@@ -54,17 +55,19 @@
fn parse_arg_ro_fds(arg: &str) -> Result<(i32, FdConfig)> {
let result: Result<Vec<i32>, _> = arg.split(':').map(|x| x.parse::<i32>()).collect();
let fds = result?;
- if fds.len() > 3 {
+ if fds.len() > 2 {
bail!("Too many options: {}", arg);
}
Ok((
fds[0],
FdConfig::Readonly {
file: fd_to_file(fds[0])?,
- // Alternative Merkle tree, if provided
- alt_merkle_tree: fds.get(1).map(|fd| fd_to_file(*fd)).transpose()?,
- // Alternative signature, if provided
- alt_signature: fds.get(2).map(|fd| fd_to_file(*fd)).transpose()?,
+ // Alternative metadata source, if provided
+ alt_metadata: fds
+ .get(1)
+ .map(|fd| fd_to_file(*fd))
+ .transpose()?
+ .and_then(|f| parse_fsverity_metadata(f).ok()),
},
))
}
diff --git a/authfs/src/auth.rs b/authfs/src/auth.rs
index 71ad858..729b4d2 100644
--- a/authfs/src/auth.rs
+++ b/authfs/src/auth.rs
@@ -20,7 +20,7 @@
// verify the signature, not the full certificate chain.
pub trait Authenticator {
- fn verify(&self, signature: &[u8], signed_data: &[u8]) -> io::Result<bool>;
+ fn verify(&self, signature: Option<&[u8]>, signed_data: &[u8]) -> io::Result<bool>;
}
pub struct FakeAuthenticator {
@@ -39,7 +39,7 @@
}
impl Authenticator for FakeAuthenticator {
- fn verify(&self, _signature_pem: &[u8], _signed_data: &[u8]) -> io::Result<bool> {
+ fn verify(&self, _signature_pem: Option<&[u8]>, _signed_data: &[u8]) -> io::Result<bool> {
Ok(self.should_allow)
}
}
diff --git a/authfs/src/fsverity/metadata/Android.bp b/authfs/src/fsverity/metadata/Android.bp
new file mode 100644
index 0000000..39ce515
--- /dev/null
+++ b/authfs/src/fsverity/metadata/Android.bp
@@ -0,0 +1,20 @@
+rust_bindgen {
+ name: "libauthfs_fsverity_metadata_bindgen",
+ wrapper_src: "metadata.hpp",
+ crate_name: "authfs_fsverity_metadata_bindgen",
+ source_stem: "metadata_bindings",
+ apex_available: ["com.android.virt"],
+}
+
+rust_library {
+ name: "libauthfs_fsverity_metadata",
+ crate_name: "authfs_fsverity_metadata",
+ srcs: [
+ "metadata.rs",
+ ],
+ rustlibs: [
+ "libauthfs_fsverity_metadata_bindgen",
+ ],
+ edition: "2018",
+ apex_available: ["com.android.virt"],
+}
diff --git a/authfs/src/fsverity/metadata/metadata.hpp b/authfs/src/fsverity/metadata/metadata.hpp
new file mode 100644
index 0000000..7bbd3da
--- /dev/null
+++ b/authfs/src/fsverity/metadata/metadata.hpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef AUTHFS_FSVERITY_METADATA_H
+#define AUTHFS_FSVERITY_METADATA_H
+
+// This file contains the format of fs-verity metadata (.fsv_meta).
+//
+// The header format of .fsv_meta is:
+//
+// +-----------+---------------------------------------------+------------+
+// | Address | Description | Size |
+// +-----------+---------------------------------------------+------------+
+// | 0x0000 | 32-bit LE, version of the format | 4 |
+// | | | |
+// | 0x0004 | fsverity_descriptor (see linux/fsverity.h) | 256 |
+// | | | |
+// | 0x0104 | 32-bit LE, type of signature | 4 |
+// | | (0: NONE, 1: PKCS7, 2: RAW) | |
+// | | | |
+// | 0x0108 | 32-bit LE, size of signature | 4 |
+// | | | |
+// | 0x010C | signature | See 0x0108 |
+// +-----------+---------------------------------------------+------------+
+//
+// After the header, merkle tree dump exists at the first 4K boundary. Usually it's 0x1000, but it
+// could be, for example, 0x2000 or 0x3000, depending on the size of header.
+//
+// TODO(b/193113326): sync with build/make/tools/releasetools/fsverity_metadata_generator.py
+
+#include <stddef.h>
+#include <stdint.h>
+#include <linux/fsverity.h>
+
+const uint64_t CHUNK_SIZE = 4096;
+
+enum class FSVERITY_SIGNATURE_TYPE : __le32 {
+ NONE = 0,
+ PKCS7 = 1,
+ RAW = 2,
+};
+
+struct fsverity_metadata_header {
+ __le32 version;
+ fsverity_descriptor descriptor;
+ FSVERITY_SIGNATURE_TYPE signature_type;
+ __le32 signature_size;
+} __attribute__((packed));
+
+#endif // AUTHFS_FSVERITY_METADATA_H
diff --git a/authfs/src/fsverity/metadata/metadata.rs b/authfs/src/fsverity/metadata/metadata.rs
new file mode 100644
index 0000000..0092bee
--- /dev/null
+++ b/authfs/src/fsverity/metadata/metadata.rs
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//! Rust bindgen interface for FSVerity Metadata file (.fsv_meta)
+use authfs_fsverity_metadata_bindgen::{
+ fsverity_metadata_header, FSVERITY_SIGNATURE_TYPE_NONE, FSVERITY_SIGNATURE_TYPE_PKCS7,
+ FSVERITY_SIGNATURE_TYPE_RAW,
+};
+
+use std::cmp::min;
+use std::os::unix::fs::MetadataExt;
+
+/// Structure for parsed metadata.
+pub struct FSVerityMetadata {
+ /// Header for the metadata.
+ pub header: fsverity_metadata_header,
+
+ /// Optional signature for the metadata.
+ pub signature: Option<Vec<u8>>,
+
+ metadata_file: File,
+
+ merkle_tree_offset: u64,
+}
+
+impl FSVerityMetadata {
+ /// Read the raw Merkle tree from the metadata, if it exists. The API semantics is similar to a
+ /// regular pread(2), and may not return full requested buffer.
+ pub fn read_merkle_tree(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
+ let start = self.merkle_tree_offset + offset;
+ let end = min(self.metadata_file.metadata()?.size(), start + buf.len() as u64);
+ let read_size = (end - start) as usize;
+ debug_assert!(read_size <= buf.len());
+ self.metadata_file.read_exact_at(&mut buf[..read_size], start)?;
+ Ok(read_size)
+ }
+}
+
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::{self, Read, Seek};
+use std::mem::{size_of, zeroed};
+use std::os::unix::fs::FileExt;
+use std::path::{Path, PathBuf};
+use std::slice::from_raw_parts_mut;
+
+/// Common block and page size in Linux.
+pub const CHUNK_SIZE: u64 = authfs_fsverity_metadata_bindgen::CHUNK_SIZE;
+
+/// Derive a path of metadata for a given path.
+/// e.g. "system/framework/foo.jar" -> "system/framework/foo.jar.fsv_meta"
+pub fn get_fsverity_metadata_path(path: &Path) -> PathBuf {
+ let mut os_string: OsString = path.into();
+ os_string.push(".fsv_meta");
+ os_string.into()
+}
+
+/// Parse metadata from given file, and returns a structure for the metadata.
+pub fn parse_fsverity_metadata(mut metadata_file: File) -> io::Result<Box<FSVerityMetadata>> {
+ let header_size = size_of::<fsverity_metadata_header>();
+
+ // SAFETY: the header doesn't include any pointers
+ let header: fsverity_metadata_header = unsafe {
+ let mut header: fsverity_metadata_header = zeroed();
+ let buffer = from_raw_parts_mut(
+ &mut header as *mut fsverity_metadata_header as *mut u8,
+ header_size,
+ );
+ metadata_file.read_exact(buffer)?;
+
+ // TODO(inseob): This doesn't seem ideal. Maybe we can consider nom?
+ header.version = u32::from_le(header.version);
+ header.descriptor.data_size = u64::from_le(header.descriptor.data_size);
+ header.signature_type = u32::from_le(header.signature_type);
+ header.signature_size = u32::from_le(header.signature_size);
+ header
+ };
+
+ if header.version != 1 {
+ return Err(io::Error::new(io::ErrorKind::Other, "unsupported metadata version"));
+ }
+
+ let signature = match header.signature_type {
+ FSVERITY_SIGNATURE_TYPE_NONE => None,
+ FSVERITY_SIGNATURE_TYPE_PKCS7 | FSVERITY_SIGNATURE_TYPE_RAW => {
+ // TODO: unpad pkcs7?
+ let mut buf = vec![0u8; header.signature_size as usize];
+ metadata_file.read_exact(&mut buf)?;
+ Some(buf)
+ }
+ _ => return Err(io::Error::new(io::ErrorKind::Other, "unknown signature type")),
+ };
+
+ // merkle tree is at the next 4K boundary
+ let merkle_tree_offset =
+ (metadata_file.stream_position()? + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
+
+ Ok(Box::new(FSVerityMetadata { header, signature, metadata_file, merkle_tree_offset }))
+}
diff --git a/authfs/src/fsverity/verifier.rs b/authfs/src/fsverity/verifier.rs
index 4a18c6a..1add37a 100644
--- a/authfs/src/fsverity/verifier.rs
+++ b/authfs/src/fsverity/verifier.rs
@@ -137,7 +137,7 @@
authenticator: &A,
chunked_file: F,
file_size: u64,
- sig: Vec<u8>,
+ sig: Option<&[u8]>,
merkle_tree: M,
) -> Result<VerifiedFileReader<F, M>, FsverityError> {
let mut buf = [0u8; CHUNK_SIZE as usize];
@@ -147,7 +147,7 @@
}
let root_hash = Sha256Hasher::new()?.update(&buf[..])?.finalize()?;
let formatted_digest = build_fsverity_formatted_digest(&root_hash, file_size)?;
- let valid = authenticator.verify(&sig, &formatted_digest)?;
+ let valid = authenticator.verify(sig, &formatted_digest)?;
if valid {
Ok(VerifiedFileReader { chunked_file, file_size, merkle_tree, root_hash })
} else {
@@ -175,9 +175,9 @@
use crate::auth::FakeAuthenticator;
use crate::file::ReadByChunk;
use anyhow::Result;
+ use authfs_fsverity_metadata::{parse_fsverity_metadata, FSVerityMetadata};
use std::cmp::min;
- use std::fs::{self, File};
- use std::io::Read;
+ use std::fs::File;
use std::os::unix::fs::FileExt;
struct LocalFileReader {
@@ -210,7 +210,17 @@
}
}
- type LocalVerifiedFileReader = VerifiedFileReader<LocalFileReader, LocalFileReader>;
+ type LocalVerifiedFileReader = VerifiedFileReader<LocalFileReader, MerkleTreeReader>;
+
+ pub struct MerkleTreeReader {
+ metadata: Box<FSVerityMetadata>,
+ }
+
+ impl ReadByChunk for MerkleTreeReader {
+ fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
+ self.metadata.read_merkle_tree(chunk_index * CHUNK_SIZE, buf)
+ }
+ }
fn total_chunk_number(file_size: u64) -> u64 {
(file_size + 4095) / 4096
@@ -219,28 +229,28 @@
// Returns a reader with fs-verity verification and the file size.
fn new_reader_with_fsverity(
content_path: &str,
- merkle_tree_path: &str,
- signature_path: &str,
+ metadata_path: &str,
) -> Result<(LocalVerifiedFileReader, u64)> {
let file_reader = LocalFileReader::new(File::open(content_path)?)?;
let file_size = file_reader.len();
- let merkle_tree = LocalFileReader::new(File::open(merkle_tree_path)?)?;
- let mut sig = Vec::new();
- let _ = File::open(signature_path)?.read_to_end(&mut sig)?;
+ let metadata = parse_fsverity_metadata(File::open(metadata_path)?)?;
let authenticator = FakeAuthenticator::always_succeed();
Ok((
- VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree)?,
+ VerifiedFileReader::new(
+ &authenticator,
+ file_reader,
+ file_size,
+ metadata.signature.clone().as_deref(),
+ MerkleTreeReader { metadata },
+ )?,
file_size,
))
}
#[test]
fn fsverity_verify_full_read_4k() -> Result<()> {
- let (file_reader, file_size) = new_reader_with_fsverity(
- "testdata/input.4k",
- "testdata/input.4k.merkle_dump",
- "testdata/input.4k.fsv_sig",
- )?;
+ let (file_reader, file_size) =
+ new_reader_with_fsverity("testdata/input.4k", "testdata/input.4k.fsv_meta")?;
for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
@@ -251,11 +261,8 @@
#[test]
fn fsverity_verify_full_read_4k1() -> Result<()> {
- let (file_reader, file_size) = new_reader_with_fsverity(
- "testdata/input.4k1",
- "testdata/input.4k1.merkle_dump",
- "testdata/input.4k1.fsv_sig",
- )?;
+ let (file_reader, file_size) =
+ new_reader_with_fsverity("testdata/input.4k1", "testdata/input.4k1.fsv_meta")?;
for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
@@ -266,11 +273,8 @@
#[test]
fn fsverity_verify_full_read_4m() -> Result<()> {
- let (file_reader, file_size) = new_reader_with_fsverity(
- "testdata/input.4m",
- "testdata/input.4m.merkle_dump",
- "testdata/input.4m.fsv_sig",
- )?;
+ let (file_reader, file_size) =
+ new_reader_with_fsverity("testdata/input.4m", "testdata/input.4m.fsv_meta")?;
for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
@@ -283,8 +287,7 @@
fn fsverity_verify_bad_merkle_tree() -> Result<()> {
let (file_reader, _) = new_reader_with_fsverity(
"testdata/input.4m",
- "testdata/input.4m.merkle_dump.bad", // First leaf node is corrupted.
- "testdata/input.4m.fsv_sig",
+ "testdata/input.4m.fsv_meta.bad_merkle", // First leaf node is corrupted.
)?;
// A lowest broken node (a 4K chunk that contains 128 sha256 hashes) will fail the read
@@ -304,10 +307,15 @@
let authenticator = FakeAuthenticator::always_fail();
let file_reader = LocalFileReader::new(File::open("testdata/input.4m")?)?;
let file_size = file_reader.len();
- let merkle_tree = LocalFileReader::new(File::open("testdata/input.4m.merkle_dump")?)?;
- let sig = fs::read("testdata/input.4m.fsv_sig")?;
- assert!(VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree)
- .is_err());
+ let metadata = parse_fsverity_metadata(File::open("testdata/input.4m.fsv_meta")?)?;
+ assert!(VerifiedFileReader::new(
+ &authenticator,
+ file_reader,
+ file_size,
+ metadata.signature.clone().as_deref(),
+ MerkleTreeReader { metadata },
+ )
+ .is_err());
Ok(())
}
}
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index 421cc02..858b099 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -27,7 +27,7 @@
//! of the actual file name, the exposed file names through AuthFS are currently integer, e.g.
//! /mountpoint/42.
-use anyhow::{bail, Context, Result};
+use anyhow::{bail, Result};
use log::error;
use std::convert::TryInto;
use std::path::{Path, PathBuf};
@@ -166,7 +166,7 @@
remote_fd: i32,
file_size: u64,
) -> Result<AuthFsEntry> {
- let signature = service.readFsveritySignature(remote_fd).context("Failed to read signature")?;
+ let signature = service.readFsveritySignature(remote_fd).ok();
let authenticator = FakeAuthenticator::always_succeed();
Ok(AuthFsEntry::VerifiedReadonly {
@@ -174,7 +174,7 @@
&authenticator,
RemoteFileReader::new(service.clone(), remote_fd),
file_size,
- signature,
+ signature.as_deref(),
RemoteMerkleTreeReader::new(service.clone(), remote_fd),
)?,
file_size,
diff --git a/authfs/testdata/README.md b/authfs/testdata/README.md
index 113fe62..cf641a9 100644
--- a/authfs/testdata/README.md
+++ b/authfs/testdata/README.md
@@ -1,9 +1,10 @@
fs-verity signing
=================
With a key pair, fs-verity signature can be generated by simply running
-`fsverity` command line tool from
+`fsverity_metadata_generator` command line tool, which uses
[fsverity-util](https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git).
```
-fsverity sign test.data test.data.fsv_sig --key=key.pem --cert=cert.pem
+fsverity_metadata_generator --fsverity-path {fsverity_path} --key key.pem --key-format pem \
+ --cert cert.pem --signature pkcs7 --output test.data.fsv_meta test.data
```
diff --git a/authfs/testdata/input.4k.fsv_meta b/authfs/testdata/input.4k.fsv_meta
new file mode 100644
index 0000000..d6a899e
--- /dev/null
+++ b/authfs/testdata/input.4k.fsv_meta
Binary files differ
diff --git a/authfs/testdata/input.4k.fsv_sig b/authfs/testdata/input.4k.fsv_sig
deleted file mode 100644
index 247a297..0000000
--- a/authfs/testdata/input.4k.fsv_sig
+++ /dev/null
Binary files differ
diff --git a/authfs/testdata/input.4k.merkle_dump b/authfs/testdata/input.4k.merkle_dump
deleted file mode 100644
index d93cd33..0000000
--- a/authfs/testdata/input.4k.merkle_dump
+++ /dev/null
Binary files differ
diff --git a/authfs/testdata/input.4k1.fsv_meta b/authfs/testdata/input.4k1.fsv_meta
new file mode 100644
index 0000000..acbbb40
--- /dev/null
+++ b/authfs/testdata/input.4k1.fsv_meta
Binary files differ
diff --git a/authfs/testdata/input.4k1.fsv_sig b/authfs/testdata/input.4k1.fsv_sig
deleted file mode 100644
index 02f0056..0000000
--- a/authfs/testdata/input.4k1.fsv_sig
+++ /dev/null
Binary files differ
diff --git a/authfs/testdata/input.4k1.merkle_dump b/authfs/testdata/input.4k1.merkle_dump
deleted file mode 100644
index 4ebdc3c..0000000
--- a/authfs/testdata/input.4k1.merkle_dump
+++ /dev/null
Binary files differ
diff --git a/authfs/testdata/input.4m.merkle_dump.bad b/authfs/testdata/input.4m.fsv_meta
similarity index 89%
copy from authfs/testdata/input.4m.merkle_dump.bad
copy to authfs/testdata/input.4m.fsv_meta
index eec67ca..447a780 100644
--- a/authfs/testdata/input.4m.merkle_dump.bad
+++ b/authfs/testdata/input.4m.fsv_meta
Binary files differ
diff --git a/authfs/testdata/input.4m.merkle_dump.bad b/authfs/testdata/input.4m.fsv_meta.bad_merkle
similarity index 89%
rename from authfs/testdata/input.4m.merkle_dump.bad
rename to authfs/testdata/input.4m.fsv_meta.bad_merkle
index eec67ca..fd61c3e 100644
--- a/authfs/testdata/input.4m.merkle_dump.bad
+++ b/authfs/testdata/input.4m.fsv_meta.bad_merkle
Binary files differ
diff --git a/authfs/testdata/input.4m.fsv_sig b/authfs/testdata/input.4m.fsv_sig
deleted file mode 100644
index 12adca3..0000000
--- a/authfs/testdata/input.4m.fsv_sig
+++ /dev/null
Binary files differ
diff --git a/authfs/testdata/input.4m.merkle_dump b/authfs/testdata/input.4m.merkle_dump
deleted file mode 100644
index b369bab..0000000
--- a/authfs/testdata/input.4m.merkle_dump
+++ /dev/null
Binary files differ
diff --git a/authfs/tests/AndroidTest.xml b/authfs/tests/AndroidTest.xml
index 643e2b4..cc358f2 100644
--- a/authfs/tests/AndroidTest.xml
+++ b/authfs/tests/AndroidTest.xml
@@ -40,20 +40,14 @@
<option name="push-file" key="input.4m" value="/data/local/tmp/authfs/input.4m" />
<option name="push-file" key="input.4k1" value="/data/local/tmp/authfs/input.4k1" />
<option name="push-file" key="input.4k" value="/data/local/tmp/authfs/input.4k" />
- <option name="push-file" key="input.4m.fsv_sig"
- value="/data/local/tmp/authfs/input.4m.fsv_sig" />
- <option name="push-file" key="input.4k1.fsv_sig"
- value="/data/local/tmp/authfs/input.4k1.fsv_sig" />
- <option name="push-file" key="input.4k.fsv_sig"
- value="/data/local/tmp/authfs/input.4k.fsv_sig" />
- <option name="push-file" key="input.4m.merkle_dump"
- value="/data/local/tmp/authfs/input.4m.merkle_dump" />
- <option name="push-file" key="input.4m.merkle_dump.bad"
- value="/data/local/tmp/authfs/input.4m.merkle_dump.bad" />
- <option name="push-file" key="input.4k1.merkle_dump"
- value="/data/local/tmp/authfs/input.4k1.merkle_dump" />
- <option name="push-file" key="input.4k.merkle_dump"
- value="/data/local/tmp/authfs/input.4k.merkle_dump" />
+ <option name="push-file" key="input.4m.fsv_meta"
+ value="/data/local/tmp/authfs/input.4m.fsv_meta" />
+ <option name="push-file" key="input.4k1.fsv_meta"
+ value="/data/local/tmp/authfs/input.4k1.fsv_meta" />
+ <option name="push-file" key="input.4k.fsv_meta"
+ value="/data/local/tmp/authfs/input.4k.fsv_meta" />
+ <option name="push-file" key="input.4m.fsv_meta.bad_merkle"
+ value="/data/local/tmp/authfs/input.4m.fsv_meta.bad_merkle" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 101a349..acaead0 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -161,9 +161,8 @@
public void testReadWithFsverityVerification_RemoteFile() throws Exception {
// Setup
runFdServerOnAndroid(
- "--open-ro 3:input.4m --open-ro 4:input.4m.merkle_dump --open-ro 5:input.4m.fsv_sig"
- + " --open-ro 6:input.4m",
- "--ro-fds 3:4:5 --ro-fds 6");
+ "--open-ro 3:input.4m --open-ro 4:input.4m.fsv_meta --open-ro 6:input.4m",
+ "--ro-fds 3:4 --ro-fds 6");
runAuthFsOnMicrodroid(
"--remote-ro-file-unverified 6 --remote-ro-file 3:cert.der --cid "
@@ -186,10 +185,9 @@
public void testReadWithFsverityVerification_RemoteSmallerFile() throws Exception {
// Setup
runFdServerOnAndroid(
- "--open-ro 3:input.4k --open-ro 4:input.4k.merkle_dump --open-ro"
- + " 5:input.4k.fsv_sig --open-ro 6:input.4k1 --open-ro 7:input.4k1.merkle_dump"
- + " --open-ro 8:input.4k1.fsv_sig",
- "--ro-fds 3:4:5 --ro-fds 6:7:8");
+ "--open-ro 3:input.4k --open-ro 4:input.4k.fsv_meta --open-ro"
+ + " 6:input.4k1 --open-ro 7:input.4k1.fsv_meta",
+ "--ro-fds 3:4 --ro-fds 6:7");
runAuthFsOnMicrodroid(
"--remote-ro-file 3:cert.der --remote-ro-file 6:cert.der --cid " + VMADDR_CID_HOST);
@@ -209,9 +207,8 @@
public void testReadWithFsverityVerification_TamperedMerkleTree() throws Exception {
// Setup
runFdServerOnAndroid(
- "--open-ro 3:input.4m --open-ro 4:input.4m.merkle_dump.bad "
- + "--open-ro 5:input.4m.fsv_sig",
- "--ro-fds 3:4:5");
+ "--open-ro 3:input.4m --open-ro 4:input.4m.fsv_meta.bad_merkle",
+ "--ro-fds 3:4");
runAuthFsOnMicrodroid("--remote-ro-file 3:cert.der --cid " + VMADDR_CID_HOST);
// Verify
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index 194180b..395a09b 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -104,14 +104,4 @@
* @return whether the inputs are valid and correspond to each other.
*/
boolean verifySigningKey(in byte[] keyBlob, in byte[] publicKey);
-
- /**
- * Signs some data with the initialized key. The call will fail with EX_ILLEGAL_STATE if not
- * yet initialized.
- *
- * @param data The data to be signed. (Large data sizes may cause failure.)
- * @return the signature.
- */
- // STOPSHIP(b/193241041): We must not expose this from the PVM.
- byte[] sign(in byte[] data);
}
diff --git a/compos/apex/Android.bp b/compos/apex/Android.bp
index f40da9c..564a380 100644
--- a/compos/apex/Android.bp
+++ b/compos/apex/Android.bp
@@ -59,7 +59,6 @@
],
prebuilts: [
- "CompOSPayloadApp.apk.idsig",
"com.android.compos.init.rc",
],
}
diff --git a/compos/apk/Android.bp b/compos/apk/Android.bp
index 3a68b8e..c6192b9 100644
--- a/compos/apk/Android.bp
+++ b/compos/apk/Android.bp
@@ -3,42 +3,7 @@
}
android_app {
- name: "CompOSPayloadApp.unsigned",
+ name: "CompOSPayloadApp",
sdk_version: "current",
apex_available: ["com.android.compos"],
}
-
-// TODO(b/190409306) this is temporary until we have a solid way to pass merkle tree
-java_genrule {
- name: "CompOSPayloadApp.signing",
- out: [
- "CompOSPayloadApp.apk",
- "CompOSPayloadApp.apk.idsig",
- ],
- srcs: [":CompOSPayloadApp.unsigned"],
- tools: ["apksigner"],
- tool_files: ["test.keystore"],
- cmd: "$(location apksigner) sign " +
- "--ks $(location test.keystore) " +
- "--ks-pass=pass:testkey --key-pass=pass:testkey " +
- "--in $(in) " +
- "--out $(genDir)/CompOSPayloadApp.apk",
- // $(genDir)/CompOSPayloadApp.apk.idsig is generated implicitly
-}
-
-android_app_import {
- name: "CompOSPayloadApp",
- // Make sure the build system doesn't try to resign the APK
- dex_preopt: {
- enabled: false,
- },
- apk: ":CompOSPayloadApp.signing{CompOSPayloadApp.apk}",
- presigned: true,
- filename: "CompOSPayloadApp.apk",
- apex_available: ["com.android.compos"],
-}
-
-prebuilt_etc {
- name: "CompOSPayloadApp.apk.idsig",
- src: ":CompOSPayloadApp.signing{CompOSPayloadApp.apk.idsig}",
-}
diff --git a/compos/apk/test.keystore b/compos/apk/test.keystore
deleted file mode 100644
index 2946641..0000000
--- a/compos/apk/test.keystore
+++ /dev/null
Binary files differ
diff --git a/compos/common/Android.bp b/compos/common/Android.bp
index 7c61d94..39e7c0a 100644
--- a/compos/common/Android.bp
+++ b/compos/common/Android.bp
@@ -11,6 +11,7 @@
"android.system.virtualizationservice-rust",
"compos_aidl_interface-rust",
"libanyhow",
+ "libbinder_common",
"libbinder_rpc_unstable_bindgen",
"libbinder_rs",
"liblog_rust",
diff --git a/compos/composd/src/util.rs b/compos/common/binder.rs
similarity index 62%
rename from compos/composd/src/util.rs
rename to compos/common/binder.rs
index 54d7751..6bd3957 100644
--- a/compos/composd/src/util.rs
+++ b/compos/common/binder.rs
@@ -14,16 +14,21 @@
* limitations under the License.
*/
-use android_system_composd::binder::Result as BinderResult;
+//! Helper for converting Error types to what Binder expects
+
use anyhow::Result;
-use binder_common::new_binder_service_specific_error;
-use log::error;
+use binder::public_api::{ExceptionCode, Result as BinderResult};
+use binder_common::new_binder_exception;
+use log::warn;
use std::fmt::Debug;
+/// Convert a Result<T, E> to BinderResult<T> to allow it to be returned from a binder RPC,
+/// preserving the content as far as possible.
+/// Also log the error if there is one.
pub fn to_binder_result<T, E: Debug>(result: Result<T, E>) -> BinderResult<T> {
result.map_err(|e| {
let message = format!("{:?}", e);
- error!("Returning binder error: {}", &message);
- new_binder_service_specific_error(-1, message)
+ warn!("Returning binder error: {}", &message);
+ new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, message)
})
}
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 9c23fac..3bb066f 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -72,6 +72,7 @@
pub fn start(
service: &dyn IVirtualizationService,
instance_image: File,
+ idsig: &Path,
parameters: &VmParameters,
) -> Result<VmInstance> {
let instance_fd = ParcelFileDescriptor::new(instance_image);
@@ -83,9 +84,18 @@
.context("Failed to open config APK file")?;
let apk_fd = ParcelFileDescriptor::new(apk_fd);
- let idsig_fd = File::open(apex_dir.join("etc/CompOSPayloadApp.apk.idsig"))
- .context("Failed to open config APK idsig file")?;
- let idsig_fd = ParcelFileDescriptor::new(idsig_fd);
+ if !idsig.exists() {
+ // Prepare idsig file via VirtualizationService
+ let idsig_file = File::create(idsig).context("Failed to create idsig file")?;
+ let idsig_fd = ParcelFileDescriptor::new(idsig_file);
+ service
+ .createOrUpdateIdsigFile(&apk_fd, &idsig_fd)
+ .context("Failed to update idsig file")?;
+ }
+
+ // Open idsig as read-only
+ let idsig_file = File::open(idsig).context("Failed to open idsig file")?;
+ let idsig_fd = ParcelFileDescriptor::new(idsig_file);
let (console_fd, log_fd, debug_level) = if parameters.debug_mode {
// Console output and the system log output from the VM are redirected to file.
diff --git a/compos/common/lib.rs b/compos/common/lib.rs
index 5c28379..9a4d0e3 100644
--- a/compos/common/lib.rs
+++ b/compos/common/lib.rs
@@ -16,6 +16,7 @@
//! Common items used by CompOS server and/or clients
+pub mod binder;
pub mod compos_client;
pub mod odrefresh;
pub mod timeouts;
@@ -54,6 +55,9 @@
/// The file that holds the instance image for a CompOS instance.
pub const INSTANCE_IMAGE_FILE: &str = "instance.img";
+/// The file that holds the idsig for the CompOS Payload APK.
+pub const IDSIG_FILE: &str = "idsig";
+
/// The path within our config APK of our default VM configuration file, used at boot time.
pub const DEFAULT_VM_CONFIG_PATH: &str = "assets/vm_config.json";
diff --git a/compos/common/odrefresh.rs b/compos/common/odrefresh.rs
index 7838b69..7fe6ed5 100644
--- a/compos/common/odrefresh.rs
+++ b/compos/common/odrefresh.rs
@@ -16,12 +16,15 @@
//! Helpers for running odrefresh
+use anyhow::{anyhow, Result};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
/// The path to the odrefresh binary
pub const ODREFRESH_PATH: &str = "/apex/com.android.art/bin/odrefresh";
+// The highest "standard" exit code defined in sysexits.h (as EX__MAX); odrefresh error codes
+// start above here to avoid clashing.
// TODO: What if this changes?
const EX_MAX: i8 = 78;
@@ -30,7 +33,7 @@
#[repr(i8)]
pub enum ExitCode {
/// No compilation required, all artifacts look good
- Okay = 0i8,
+ Okay = 0,
/// Compilation required
CompilationRequired = EX_MAX + 1,
/// New artifacts successfully generated
@@ -43,7 +46,8 @@
impl ExitCode {
/// Map an integer to the corresponding ExitCode enum, if there is one
- pub fn from_i32(exit_code: i32) -> Option<Self> {
+ pub fn from_i32(exit_code: i32) -> Result<Self> {
FromPrimitive::from_i32(exit_code)
+ .ok_or_else(|| anyhow!("Unexpected odrefresh exit code: {}", exit_code))
}
}
diff --git a/compos/compos_key_cmd/compos_key_cmd.cpp b/compos/compos_key_cmd/compos_key_cmd.cpp
index f8b3d16..0305cd4 100644
--- a/compos/compos_key_cmd/compos_key_cmd.cpp
+++ b/compos/compos_key_cmd/compos_key_cmd.cpp
@@ -58,6 +58,7 @@
using aidl::android::system::virtualizationservice::VirtualMachineConfig;
using aidl::com::android::compos::CompOsKeyData;
using aidl::com::android::compos::ICompOsService;
+using android::base::Dirname;
using android::base::ErrnoError;
using android::base::Error;
using android::base::Fdopen;
@@ -73,8 +74,6 @@
constexpr const char* kConfigApkPath =
"/apex/com.android.compos/app/CompOSPayloadApp/CompOSPayloadApp.apk";
-constexpr const char* kConfigApkIdsigPath =
- "/apex/com.android.compos/etc/CompOSPayloadApp.apk.idsig";
// These are paths inside the APK
constexpr const char* kDefaultConfigFilePath = "assets/vm_config.json";
@@ -211,6 +210,8 @@
return Error() << "Can't specify both cid and image file.";
}
+ // Start a new VM with a given instance.img
+
// We need a thread pool to receive VM callbacks.
ABinderProcess_startThreadPool();
@@ -242,10 +243,25 @@
return ErrnoError() << "Failed to open config APK";
}
+ // Prepare an idsig file
+ std::string idsigPath = Dirname(mInstanceImageFile) + "/idsig";
+ {
+ ScopedFileDescriptor idsigFd(TEMP_FAILURE_RETRY(
+ open(idsigPath.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP)));
+ if (idsigFd.get() == -1) {
+ return ErrnoError() << "Failed to create an idsig file";
+ }
+ auto status = service->createOrUpdateIdsigFile(apkFd, idsigFd);
+ if (!status.isOk()) {
+ return Error() << status.getDescription();
+ }
+ }
+
ScopedFileDescriptor idsigFd(
- TEMP_FAILURE_RETRY(open(kConfigApkIdsigPath, O_RDONLY | O_CLOEXEC)));
+ TEMP_FAILURE_RETRY(open(idsigPath.c_str(), O_RDONLY | O_CLOEXEC)));
if (idsigFd.get() == -1) {
- return ErrnoError() << "Failed to open config APK signature";
+ return ErrnoError() << "Failed to open an idsig file";
}
ScopedFileDescriptor instanceFd(
@@ -405,121 +421,6 @@
return result;
}
-static Result<std::vector<uint8_t>> computeDigest(const std::string& file) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
- if (!fd.ok()) {
- return ErrnoError() << "Failed to open";
- }
-
- struct stat filestat;
- if (fstat(fd, &filestat) != 0) {
- return ErrnoError() << "Failed to fstat";
- }
-
- struct libfsverity_merkle_tree_params params = {
- .version = 1,
- .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
- .file_size = static_cast<uint64_t>(filestat.st_size),
- .block_size = 4096,
- };
-
- auto read_callback = [](void* file, void* buf, size_t count) {
- int* fd = static_cast<int*>(file);
- if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return -errno;
- return 0;
- };
-
- struct libfsverity_digest* digest;
- int ret = libfsverity_compute_digest(&fd, read_callback, ¶ms, &digest);
- if (ret < 0) {
- return Error(-ret) << "Failed to compute fs-verity digest";
- }
- std::unique_ptr<libfsverity_digest, decltype(&std::free)> digestOwner{digest, std::free};
-
- return std::vector(&digest->digest[0], &digest->digest[digest->digest_size]);
-}
-
-static std::string toHex(const std::vector<uint8_t>& digest) {
- std::stringstream ss;
- for (auto it = digest.begin(); it != digest.end(); ++it) {
- ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
- }
- return ss.str();
-}
-
-static Result<void> signInfo(TargetVm& vm, const std::string& blob_file,
- const std::string& info_file, const std::vector<std::string>& files) {
- unique_fd info_fd(
- TEMP_FAILURE_RETRY(open(info_file.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP)));
- if (!info_fd.ok()) {
- return ErrnoError() << "Unable to create " << info_file;
- }
-
- std::string signature_file = info_file + ".signature";
- unique_fd signature_fd(TEMP_FAILURE_RETRY(open(signature_file.c_str(),
- O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP)));
- if (!signature_fd.ok()) {
- return ErrnoError() << "Unable to create " << signature_file;
- }
-
- auto cid = vm.resolveCid();
- if (!cid.ok()) {
- return cid.error();
- }
- auto service = getService(*cid);
- if (!service) {
- return Error() << "No service";
- }
-
- auto blob = readBytesFromFile(blob_file);
- if (!blob.ok()) {
- return blob.error();
- }
-
- auto initialized = service->initializeSigningKey(blob.value());
- if (!initialized.isOk()) {
- return Error() << "Failed to initialize signing key: " << initialized.getDescription();
- }
-
- std::map<std::string, std::string> file_digests;
-
- for (auto& file : files) {
- auto digest = computeDigest(file);
- if (!digest.ok()) {
- return digest.error();
- }
- file_digests.emplace(file, toHex(*digest));
- }
-
- OdsignInfo info;
- info.mutable_file_hashes()->insert(file_digests.begin(), file_digests.end());
-
- std::vector<uint8_t> serialized(info.ByteSizeLong());
- if (!info.SerializeToArray(serialized.data(), serialized.size())) {
- return Error() << "Failed to serialize protobuf";
- }
-
- if (!WriteFully(info_fd, serialized.data(), serialized.size()) ||
- close(info_fd.release()) != 0) {
- return Error() << "Failed to write info file";
- }
-
- std::vector<uint8_t> signature;
- auto status = service->sign(serialized, &signature);
- if (!status.isOk()) {
- return Error() << "Failed to sign: " << status.getDescription();
- }
-
- if (!WriteFully(signature_fd, signature.data(), signature.size()) ||
- close(signature_fd.release()) != 0) {
- return Error() << "Failed to write signature";
- }
-
- return {};
-}
-
static Result<void> initializeKey(TargetVm& vm, const std::string& blob_file) {
auto cid = vm.resolveCid();
if (!cid.ok()) {
@@ -628,17 +529,6 @@
} else {
std::cerr << result.error() << '\n';
}
- } else if (argc >= 5 && argv[1] == "sign-info"sv) {
- const std::string blob_file = argv[2];
- const std::string info_file = argv[3];
- const std::vector<std::string> files{&argv[4], &argv[argc]};
- auto result = signInfo(vm, blob_file, info_file, files);
- if (result.ok()) {
- std::cerr << "Info file generated and signed.\n";
- return 0;
- } else {
- std::cerr << result.error() << '\n';
- }
} else if (argc == 3 && argv[1] == "init-key"sv) {
auto result = initializeKey(vm, argv[2]);
if (result.ok()) {
@@ -662,9 +552,6 @@
<< " verify <blob file> <public key file> Verify that the content of the\n"
<< " specified private key blob and public key files are valid.\n "
<< " init-key <blob file> Initialize the service key.\n"
- << " sign-info <blob file> <info file> <files to be signed> Generate\n"
- << " an info file listing the paths and root digests of each of the files to\n"
- << " be signed, along with a signature of that file.\n"
<< "\n"
<< "OPTIONS: --log <log file> --debug --staged\n"
<< " (--cid <cid> | --start <image file>)\n"
diff --git a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
index ec5f2f5..8156265 100644
--- a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
+++ b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
@@ -31,19 +31,6 @@
ICompilationTask startStagedApexCompile(ICompilationTaskCallback callback);
/**
- * Run "odrefresh --dalvik-cache=pending-test --force-compile" in a test instance of CompOS.
- *
- * This compiles BCP extensions and system server, even if the system artifacts are up to date,
- * and writes the results to a test directory to avoid disrupting any real artifacts in
- * existence.
- *
- * Compilation continues in the background, and success/failure is reported via the supplied
- * callback, unless the returned ICompilationTask is cancelled. The caller should maintain
- * a reference to the ICompilationTask until compilation completes or is cancelled.
- */
- ICompilationTask startTestCompile(ICompilationTaskCallback callback);
-
- /**
* Run odrefresh in a test instance of CompOS until completed or failed.
*
* This compiles BCP extensions and system server, even if the system artifacts are up to date,
@@ -54,5 +41,5 @@
* callback, unless the returned ICompilationTask is cancelled. The caller should maintain
* a reference to the ICompilationTask until compilation completes or is cancelled.
*/
- ICompilationTask startAsyncOdrefresh(ICompilationTaskCallback callback);
+ ICompilationTask startTestCompile(ICompilationTaskCallback callback);
}
diff --git a/compos/composd/src/compilation_task.rs b/compos/composd/src/compilation_task.rs
deleted file mode 100644
index 871c4fb..0000000
--- a/compos/composd/src/compilation_task.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.
- */
-
-use crate::instance_starter::CompOsInstance;
-use crate::odrefresh::Odrefresh;
-use android_system_composd::aidl::android::system::composd::{
- ICompilationTask::ICompilationTask, ICompilationTaskCallback::ICompilationTaskCallback,
-};
-use android_system_composd::binder::{Interface, Result as BinderResult, Strong};
-use anyhow::Result;
-use compos_common::odrefresh::ExitCode;
-use log::{error, warn};
-use std::sync::{Arc, Mutex};
-use std::thread;
-
-// TODO: Delete
-
-#[derive(Clone)]
-pub struct CompilationTask {
- running_task: Arc<Mutex<Option<RunningTask>>>,
-}
-
-impl Interface for CompilationTask {}
-
-impl ICompilationTask for CompilationTask {
- fn cancel(&self) -> BinderResult<()> {
- let task = self.take();
- if let Some(task) = task {
- if let Err(e) = task.odrefresh.kill() {
- warn!("Failed to kill running task: {:?}", e)
- }
- }
- Ok(())
- }
-}
-
-impl CompilationTask {
- /// Return the current running task, if any, removing it from this CompilationTask.
- /// Once removed, meaning the task has ended or been canceled, further calls will always return
- /// None.
- fn take(&self) -> Option<RunningTask> {
- self.running_task.lock().unwrap().take()
- }
-
- pub fn start_staged_apex_compile(
- comp_os: Arc<CompOsInstance>,
- callback: &Strong<dyn ICompilationTaskCallback>,
- ) -> Result<CompilationTask> {
- // TODO: Write to pending
- // TODO: Delete any existing artifacts
- let odrefresh = Odrefresh::spawn_compile("test-artifacts")?;
- let odrefresh = Arc::new(odrefresh);
- let task =
- RunningTask { odrefresh: odrefresh.clone(), comp_os, callback: callback.clone() };
- let task = CompilationTask { running_task: Arc::new(Mutex::new(Some(task))) };
-
- task.clone().start_waiting_thread(odrefresh);
-
- Ok(task)
- }
-
- pub fn start_test_compile(
- comp_os: Arc<CompOsInstance>,
- callback: &Strong<dyn ICompilationTaskCallback>,
- ) -> Result<CompilationTask> {
- let odrefresh = Odrefresh::spawn_forced_compile("test-artifacts")?;
- let odrefresh = Arc::new(odrefresh);
- let task =
- RunningTask { odrefresh: odrefresh.clone(), comp_os, callback: callback.clone() };
- let task = CompilationTask { running_task: Arc::new(Mutex::new(Some(task))) };
-
- task.clone().start_waiting_thread(odrefresh);
-
- Ok(task)
- }
-
- fn start_waiting_thread(self, odrefresh: Arc<Odrefresh>) {
- thread::spawn(move || {
- let exit_code = odrefresh.wait_for_exit();
- let task = self.take();
- // We don't do the callback if cancel has already happened.
- if let Some(task) = task {
- let result = match exit_code {
- Ok(ExitCode::CompilationSuccess) => task.callback.onSuccess(),
- Ok(exit_code) => {
- error!("Unexpected odrefresh result: {:?}", exit_code);
- task.callback.onFailure()
- }
- Err(e) => {
- error!("Running odrefresh failed: {:?}", e);
- task.callback.onFailure()
- }
- };
- if let Err(e) = result {
- warn!("Failed to deliver callback: {:?}", e);
- }
- }
- });
- }
-}
-
-struct RunningTask {
- odrefresh: Arc<Odrefresh>,
- callback: Strong<dyn ICompilationTaskCallback>,
- #[allow(dead_code)] // Keeps the CompOS VM alive
- comp_os: Arc<CompOsInstance>,
-}
diff --git a/compos/composd/src/composd_main.rs b/compos/composd/src/composd_main.rs
index 2915a58..df088bc 100644
--- a/compos/composd/src/composd_main.rs
+++ b/compos/composd/src/composd_main.rs
@@ -18,15 +18,12 @@
//! responsible for managing the lifecycle of the CompOS VM instances, providing key management for
//! them, and orchestrating trusted compilation.
-mod compilation_task;
mod fd_server_helper;
mod instance_manager;
mod instance_starter;
mod internal_service;
-mod odrefresh;
mod odrefresh_task;
mod service;
-mod util;
use crate::instance_manager::InstanceManager;
use android_system_composd::binder::{register_lazy_service, ProcessState};
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index 8189fe0..91a0e61 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -26,7 +26,7 @@
use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
use compos_common::compos_client::{VmInstance, VmParameters};
use compos_common::{
- COMPOS_DATA_ROOT, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
+ COMPOS_DATA_ROOT, IDSIG_FILE, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
};
use log::{info, warn};
use std::env;
@@ -51,6 +51,7 @@
instance_name: String,
instance_root: PathBuf,
instance_image: PathBuf,
+ idsig: PathBuf,
key_blob: PathBuf,
public_key: PathBuf,
vm_parameters: VmParameters,
@@ -59,14 +60,16 @@
impl InstanceStarter {
pub fn new(instance_name: &str, vm_parameters: VmParameters) -> Self {
let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
- let instant_root_path = instance_root.as_path();
- let instance_image = instant_root_path.join(INSTANCE_IMAGE_FILE);
- let key_blob = instant_root_path.join(PRIVATE_KEY_BLOB_FILE);
- let public_key = instant_root_path.join(PUBLIC_KEY_FILE);
+ let instance_root_path = instance_root.as_path();
+ let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE);
+ let idsig = instance_root_path.join(IDSIG_FILE);
+ let key_blob = instance_root_path.join(PRIVATE_KEY_BLOB_FILE);
+ let public_key = instance_root_path.join(PUBLIC_KEY_FILE);
Self {
instance_name: instance_name.to_owned(),
instance_root,
instance_image,
+ idsig,
key_blob,
public_key,
vm_parameters,
@@ -124,6 +127,8 @@
let _ = fs::create_dir(&self.instance_root);
self.create_instance_image(virtualization_service)?;
+ // Delete existing idsig file. Ignore error in case idsig doesn't exist.
+ let _ = fs::remove_file(&self.idsig);
let compos_instance = self.start_vm(virtualization_service)?;
let service = &compos_instance.service;
@@ -170,9 +175,13 @@
.write(true)
.open(&self.instance_image)
.context("Failed to open instance image")?;
- let vm_instance =
- VmInstance::start(virtualization_service, instance_image, &self.vm_parameters)
- .context("Starting VM")?;
+ let vm_instance = VmInstance::start(
+ virtualization_service,
+ instance_image,
+ &self.idsig,
+ &self.vm_parameters,
+ )
+ .context("Starting VM")?;
let service = vm_instance.get_service().context("Connecting to CompOS")?;
Ok(CompOsInstance { vm_instance, service, lazy_service_guard: Default::default() })
}
diff --git a/compos/composd/src/internal_service.rs b/compos/composd/src/internal_service.rs
index ebebaae..3fad6d4 100644
--- a/compos/composd/src/internal_service.rs
+++ b/compos/composd/src/internal_service.rs
@@ -17,7 +17,7 @@
//! Implementation of ICompilationInternal, called from odrefresh during compilation.
use crate::instance_manager::InstanceManager;
-use crate::util::to_binder_result;
+use compos_common::binder::to_binder_result;
use android_system_composd_internal::aidl::android::system::composd::internal::ICompilationInternal::{
BnCompilationInternal, ICompilationInternal,
};
diff --git a/compos/composd/src/odrefresh.rs b/compos/composd/src/odrefresh.rs
deleted file mode 100644
index f06a4b2..0000000
--- a/compos/composd/src/odrefresh.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Handle the details of executing odrefresh to generate compiled artifacts.
-
-// TODO: Delete
-
-use anyhow::{bail, Context, Result};
-use compos_common::odrefresh::{ExitCode, ODREFRESH_PATH};
-use compos_common::timeouts::{need_extra_time, EXTENDED_TIMEOUTS};
-use compos_common::VMADDR_CID_ANY;
-use shared_child::SharedChild;
-use std::process::Command;
-
-pub struct Odrefresh {
- child: SharedChild,
-}
-
-impl Odrefresh {
- pub fn spawn_compile(target_dir: &str) -> Result<Self> {
- Self::spawn_odrefresh(target_dir, "--compile")
- }
-
- pub fn spawn_forced_compile(target_dir: &str) -> Result<Self> {
- Self::spawn_odrefresh(target_dir, "--force-compile")
- }
-
- fn spawn_odrefresh(target_dir: &str, compile_arg: &str) -> Result<Self> {
- // We don`t need to capture stdout/stderr - odrefresh writes to the log
- let mut cmdline = Command::new(ODREFRESH_PATH);
- if need_extra_time()? {
- cmdline
- .arg(format!(
- "--max-execution-seconds={}",
- EXTENDED_TIMEOUTS.odrefresh_max_execution_time.as_secs()
- ))
- .arg(format!(
- "--max-child-process-seconds={}",
- EXTENDED_TIMEOUTS.odrefresh_max_child_process_time.as_secs()
- ));
- }
- cmdline
- .arg(format!("--use-compilation-os={}", VMADDR_CID_ANY as i32))
- .arg(format!("--dalvik-cache={}", target_dir))
- .arg(compile_arg);
- let child = SharedChild::spawn(&mut cmdline).context("Running odrefresh")?;
- Ok(Odrefresh { child })
- }
-
- pub fn wait_for_exit(&self) -> Result<ExitCode> {
- // No timeout here - but clients can kill the process, which will end the wait.
- let status = self.child.wait()?;
- if let Some(exit_code) = status.code().and_then(ExitCode::from_i32) {
- Ok(exit_code)
- } else {
- bail!("odrefresh exited with {}", status)
- }
- }
-
- pub fn kill(&self) -> Result<()> {
- self.child.kill().context("Killing odrefresh process failed")
- }
-}
diff --git a/compos/composd/src/odrefresh_task.rs b/compos/composd/src/odrefresh_task.rs
index bcdf2f4..56b697e 100644
--- a/compos/composd/src/odrefresh_task.rs
+++ b/compos/composd/src/odrefresh_task.rs
@@ -22,7 +22,7 @@
ICompilationTask::ICompilationTask, ICompilationTaskCallback::ICompilationTaskCallback,
};
use android_system_composd::binder::{Interface, Result as BinderResult, Strong};
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{Context, Result};
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
use compos_common::odrefresh::ExitCode;
use log::{error, warn};
@@ -113,7 +113,10 @@
// (and can't see the existing one, since authfs doesn't show it existing files in an output
// directory).
let target_path = output_root.join(target_dir_name);
- remove_dir_all(&target_path).with_context(|| anyhow!("Deleting {}", target_path.display()))?;
+ if target_path.exists() {
+ remove_dir_all(&target_path)
+ .with_context(|| format!("Failed to delete {}", target_path.display()))?;
+ }
let staging_dir = open_dir(composd_native::palette_create_odrefresh_staging_directory()?)?;
let system_dir = open_dir(Path::new("/system"))?;
@@ -137,11 +140,7 @@
)?;
drop(fd_server_raii);
- if let Some(exit_code) = ExitCode::from_i32(exit_code.into()) {
- Ok(exit_code)
- } else {
- bail!("odrefresh exited with {}", exit_code)
- }
+ ExitCode::from_i32(exit_code.into())
}
/// Returns an owned FD of the directory. It currently returns a `File` as a FD owner, but
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 23c411b..093e428 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -17,10 +17,8 @@
//! Implementation of IIsolatedCompilationService, called from system server when compilation is
//! desired.
-use crate::compilation_task::CompilationTask;
use crate::instance_manager::InstanceManager;
use crate::odrefresh_task::OdrefreshTask;
-use crate::util::to_binder_result;
use android_system_composd::aidl::android::system::composd::{
ICompilationTask::{BnCompilationTask, ICompilationTask},
ICompilationTaskCallback::ICompilationTaskCallback,
@@ -30,6 +28,7 @@
self, BinderFeatures, ExceptionCode, Interface, Status, Strong, ThreadState,
};
use anyhow::{Context, Result};
+use compos_common::binder::to_binder_result;
use rustutils::{users::AID_ROOT, users::AID_SYSTEM};
use std::sync::Arc;
@@ -62,14 +61,6 @@
check_permissions()?;
to_binder_result(self.do_start_test_compile(callback))
}
-
- fn startAsyncOdrefresh(
- &self,
- callback: &Strong<dyn ICompilationTaskCallback>,
- ) -> binder::Result<Strong<dyn ICompilationTask>> {
- check_permissions()?;
- to_binder_result(self.do_start_async_odrefresh(callback))
- }
}
impl IsolatedCompilationService {
@@ -80,7 +71,9 @@
// TODO: Try to start the current instance with staged APEXes to see if it works?
let comp_os = self.instance_manager.start_pending_instance().context("Starting CompOS")?;
- let task = CompilationTask::start_staged_apex_compile(comp_os, callback)?;
+ // TODO: Write to compos-pending instead
+ let target_dir_name = "test-artifacts".to_owned();
+ let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
}
@@ -91,17 +84,6 @@
) -> Result<Strong<dyn ICompilationTask>> {
let comp_os = self.instance_manager.start_test_instance().context("Starting CompOS")?;
- let task = CompilationTask::start_test_compile(comp_os, callback)?;
-
- Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
- }
-
- fn do_start_async_odrefresh(
- &self,
- callback: &Strong<dyn ICompilationTaskCallback>,
- ) -> Result<Strong<dyn ICompilationTask>> {
- let comp_os = self.instance_manager.start_test_instance().context("Starting CompOS")?;
-
let target_dir_name = "test-artifacts".to_owned();
let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
diff --git a/compos/composd_cmd/composd_cmd.rs b/compos/composd_cmd/composd_cmd.rs
index 41e2b1a..546c4af 100644
--- a/compos/composd_cmd/composd_cmd.rs
+++ b/compos/composd_cmd/composd_cmd.rs
@@ -38,7 +38,7 @@
.index(1)
.takes_value(true)
.required(true)
- .possible_values(&["staged-apex-compile", "forced-compile-test", "async-odrefresh"]),
+ .possible_values(&["staged-apex-compile", "test-compile"]),
);
let args = app.get_matches();
let command = args.value_of("command").unwrap();
@@ -47,8 +47,7 @@
match command {
"staged-apex-compile" => run_staged_apex_compile()?,
- "forced-compile-test" => run_forced_compile_for_test()?,
- "async-odrefresh" => run_async_odrefresh_for_test()?,
+ "test-compile" => run_test_compile()?,
_ => panic!("Unexpected command {}", command),
}
@@ -109,14 +108,10 @@
run_async_compilation(|service, callback| service.startStagedApexCompile(callback))
}
-fn run_forced_compile_for_test() -> Result<()> {
+fn run_test_compile() -> Result<()> {
run_async_compilation(|service, callback| service.startTestCompile(callback))
}
-fn run_async_odrefresh_for_test() -> Result<()> {
- run_async_compilation(|service, callback| service.startAsyncOdrefresh(callback))
-}
-
fn run_async_compilation<F>(start_compile_fn: F) -> Result<()>
where
F: FnOnce(
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index 7eaae5d..2ca4dd4 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -148,8 +148,7 @@
}
}?;
- let exit_code = ExitCode::from_i32(exit_code.into())
- .ok_or_else(|| anyhow!("Unexpected odrefresh exit code: {}", exit_code))?;
+ let exit_code = ExitCode::from_i32(exit_code.into())?;
info!("odrefresh exited with {:?}", exit_code);
if exit_code == ExitCode::CompilationSuccess {
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index aa4b9bd..8f1e205 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -18,8 +18,9 @@
//! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
//! actual compiler.
-use anyhow::Result;
+use anyhow::{Context, Result};
use binder_common::new_binder_exception;
+use compos_common::binder::to_binder_result;
use log::warn;
use std::default::Default;
use std::env;
@@ -68,9 +69,7 @@
fsverity_digest: &fsverity::Sha256Digest,
) -> BinderResult<Vec<u8>> {
let formatted_digest = fsverity::to_formatted_digest(fsverity_digest);
- self.new_signer()?
- .sign(&formatted_digest[..])
- .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))
+ to_binder_result(self.new_signer()?.sign(&formatted_digest[..]))
}
fn new_signer(&self) -> BinderResult<Signer> {
@@ -117,26 +116,19 @@
target_dir_name: &str,
zygote_arch: &str,
) -> BinderResult<i8> {
- let context = OdrefreshContext::new(
+ let context = to_binder_result(OdrefreshContext::new(
system_dir_fd,
output_dir_fd,
staging_dir_fd,
target_dir_name,
zygote_arch,
- )
- .map_err(|e| new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, e.to_string()))?;
+ ))?;
let authfs_service = get_authfs_service()?;
- let exit_code =
- odrefresh(&self.odrefresh_path, context, authfs_service, self.new_signer()?).map_err(
- |e| {
- warn!("odrefresh failed: {:?}", e);
- new_binder_exception(
- ExceptionCode::SERVICE_SPECIFIC,
- format!("odrefresh failed: {}", e),
- )
- },
- )?;
+ let exit_code = to_binder_result(
+ odrefresh(&self.odrefresh_path, context, authfs_service, self.new_signer()?)
+ .context("odrefresh failed"),
+ )?;
Ok(exit_code as i8)
}
@@ -146,13 +138,10 @@
fd_annotation: &FdAnnotation,
) -> BinderResult<CompilationResult> {
let authfs_service = get_authfs_service()?;
- let output =
- compile_cmd(&self.dex2oat_path, args, authfs_service, fd_annotation).map_err(|e| {
- new_binder_exception(
- ExceptionCode::SERVICE_SPECIFIC,
- format!("Compilation failed: {}", e),
- )
- })?;
+ let output = to_binder_result(
+ compile_cmd(&self.dex2oat_path, args, authfs_service, fd_annotation)
+ .context("Compilation failed"),
+ )?;
match output {
CompilerOutput::Digests { oat, vdex, image } => {
let oat_signature = self.generate_raw_fsverity_signature(&oat)?;
@@ -176,25 +165,17 @@
}
fn generateSigningKey(&self) -> BinderResult<CompOsKeyData> {
- self.key_service
- .generate()
- .map_err(|e| new_binder_exception(ExceptionCode::ILLEGAL_STATE, e.to_string()))
+ to_binder_result(self.key_service.generate())
}
fn verifySigningKey(&self, key_blob: &[u8], public_key: &[u8]) -> BinderResult<bool> {
Ok(if let Err(e) = self.key_service.verify(key_blob, public_key) {
- warn!("Signing key verification failed: {}", e.to_string());
+ warn!("Signing key verification failed: {:?}", e);
false
} else {
true
})
}
-
- fn sign(&self, data: &[u8]) -> BinderResult<Vec<u8>> {
- self.new_signer()?
- .sign(data)
- .map_err(|e| new_binder_exception(ExceptionCode::ILLEGAL_STATE, e.to_string()))
- }
}
fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
diff --git a/compos/tests/Android.bp b/compos/tests/Android.bp
index 7e00d7b..d380059 100644
--- a/compos/tests/Android.bp
+++ b/compos/tests/Android.bp
@@ -14,7 +14,4 @@
"VirtualizationTestHelper",
],
test_suites: ["general-tests"],
- data: [
- ":CompOSPayloadApp.signing",
- ],
}
diff --git a/compos/tests/java/android/compos/test/ComposKeyTestCase.java b/compos/tests/java/android/compos/test/ComposKeyTestCase.java
index eacf3fb..961f34a 100644
--- a/compos/tests/java/android/compos/test/ComposKeyTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposKeyTestCase.java
@@ -120,44 +120,15 @@
TEST_ROOT + "test_key.pubkey",
TEST_ROOT + "test_key2.blob");
assertThat(result.getStatus()).isEqualTo(CommandStatus.FAILED);
-
- // Now, continue to test the signing operation. It's the best to do this in a new test
- // method. Since we boot a VM for each test method, and booting a VM on cuttlefish/GCE is
- // very slow, a new test method unfortunately makes the whole test module to exceed the
- // timeout configured in the test infrastructure.
-
- // Generate key - should succeed
- android.run(
- COMPOS_KEY_CMD_BIN,
- "--cid " + mCid,
- "generate",
- TEST_ROOT + "test_key3.blob",
- TEST_ROOT + "test_key3.pubkey");
-
- // Generate some data to sign in a writable directory
- android.run("echo something > /data/local/tmp/something.txt");
-
- // Sign something - should succeed
- android.run(
- COMPOS_KEY_CMD_BIN,
- "--cid " + mCid,
- "sign-info",
- TEST_ROOT + "test_key3.blob",
- TEST_ROOT + "test.info",
- "/data/local/tmp/something.txt");
-
- // Check existence of the output signature - should succeed
- android.run("test -f " + TEST_ROOT + "test.info.signature");
}
private void startVm() throws Exception {
- final String apkName = "CompOSPayloadApp.apk";
final String packageName = "com.android.compos.payload";
mCid =
startMicrodroid(
getDevice(),
getBuild(),
- apkName,
+ /* apkName, no need to install */ null,
packageName,
"assets/vm_test_config.json",
/* debug */ true,
diff --git a/compos/tests/java/android/compos/test/ComposTestCase.java b/compos/tests/java/android/compos/test/ComposTestCase.java
index e64a07e..8906361 100644
--- a/compos/tests/java/android/compos/test/ComposTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposTestCase.java
@@ -104,7 +104,7 @@
long start = System.currentTimeMillis();
result =
android.runForResultWithTimeout(
- ODREFRESH_TIMEOUT_MS, COMPOSD_CMD_BIN, "async-odrefresh");
+ ODREFRESH_TIMEOUT_MS, COMPOSD_CMD_BIN, "test-compile");
long elapsed = System.currentTimeMillis() - start;
assertThat(result.getExitCode()).isEqualTo(0);
CLog.i("Comp OS compilation took " + elapsed + "ms");
@@ -123,55 +123,11 @@
android.assumeSuccess("test -f " + ODREFRESH_OUTPUT_DIR + "/compos.info.signature");
}
- @Test
- public void testOdrefreshDeprecated() throws Exception {
- CommandRunner android = new CommandRunner(getDevice());
-
- // Prepare the groundtruth. The compilation on Android should finish successfully.
- {
- long start = System.currentTimeMillis();
- CommandResult result = runOdrefresh(android, "--force-compile");
- long elapsed = System.currentTimeMillis() - start;
- assertThat(result.getExitCode()).isEqualTo(COMPILATION_SUCCESS);
- CLog.i("Local compilation took " + elapsed + "ms");
- }
-
- // Save the expected checksum for the output directory.
- String expectedChecksumSnapshot = checksumDirectoryContent(android, ODREFRESH_OUTPUT_DIR);
-
- // Let --check clean up the output.
- CommandResult result = runOdrefresh(android, "--check");
- assertThat(result.getExitCode()).isEqualTo(OKAY);
-
- // Make sure we generate a fresh instance
- android.tryRun("rm", "-rf", COMPOS_TEST_ROOT);
-
- // Expect the compilation in Compilation OS to finish successfully.
- {
- long start = System.currentTimeMillis();
- result =
- android.runForResultWithTimeout(
- ODREFRESH_TIMEOUT_MS, COMPOSD_CMD_BIN, "forced-compile-test");
- long elapsed = System.currentTimeMillis() - start;
- assertThat(result.getExitCode()).isEqualTo(0);
- CLog.i("Comp OS compilation took " + elapsed + "ms");
- }
- killVmAndReconnectAdb();
-
- // Save the actual checksum for the output directory.
- String actualChecksumSnapshot = checksumDirectoryContent(android, ODREFRESH_OUTPUT_DIR);
-
- // Expect the output to be valid.
- result = runOdrefresh(android, "--check");
- assertThat(result.getExitCode()).isEqualTo(OKAY);
-
- // Expect the output of Comp OS to be the same as compiled on Android.
- assertThat(actualChecksumSnapshot).isEqualTo(expectedChecksumSnapshot);
- }
-
private CommandResult runOdrefresh(CommandRunner android, String command) throws Exception {
return android.runForResultWithTimeout(
ODREFRESH_TIMEOUT_MS,
+ // TODO(b/210472252): Remove this when the VM handles STANDALONE_SYSTEMSERVER_JARS
+ "STANDALONE_SYSTEMSERVER_JARS=",
ODREFRESH_BIN,
"--dalvik-cache=" + TEST_ARTIFACTS_DIR,
command);
@@ -194,11 +150,6 @@
android.tryRun("rm", "-rf", "/data/misc/virtualizationservice/*");
}
- private String checksumDirectoryContent(CommandRunner runner, String path) throws Exception {
- // Sort by filename (second column) to make comparison easier.
- return runner.run("find " + path + " -type f -exec sha256sum {} \\; | sort -k2");
- }
-
private String checksumDirectoryContentPartial(CommandRunner runner, String path)
throws Exception {
// Sort by filename (second column) to make comparison easier. Filter out compos.info and
diff --git a/compos/verify_key/verify_key.rs b/compos/verify_key/verify_key.rs
index e0ed5e5..a028264 100644
--- a/compos/verify_key/verify_key.rs
+++ b/compos/verify_key/verify_key.rs
@@ -21,7 +21,7 @@
use compos_aidl_interface::binder::ProcessState;
use compos_common::compos_client::{VmInstance, VmParameters};
use compos_common::{
- COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR, INSTANCE_IMAGE_FILE, PENDING_INSTANCE_DIR,
+ COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR, IDSIG_FILE, INSTANCE_IMAGE_FILE, PENDING_INSTANCE_DIR,
PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE, TEST_INSTANCE_DIR,
};
use std::fs::{self, File};
@@ -99,6 +99,7 @@
let blob = instance_dir.join(PRIVATE_KEY_BLOB_FILE);
let public_key = instance_dir.join(PUBLIC_KEY_FILE);
let instance_image = instance_dir.join(INSTANCE_IMAGE_FILE);
+ let idsig = instance_dir.join(IDSIG_FILE);
let blob = read_small_file(blob).context("Failed to read key blob")?;
let public_key = read_small_file(public_key).context("Failed to read public key")?;
@@ -108,6 +109,7 @@
let vm_instance = VmInstance::start(
&*virtualization_service,
instance_image,
+ &idsig,
&VmParameters { debug_mode, ..Default::default() },
)?;
let service = vm_instance.get_service()?;
diff --git a/idsig/src/hashtree.rs b/idsig/src/hashtree.rs
index 1ce2879..63f83ea 100644
--- a/idsig/src/hashtree.rs
+++ b/idsig/src/hashtree.rs
@@ -100,7 +100,7 @@
// and split it.
let prev = &levels[n - 1];
let cur_and_prev = &mut hash_tree[cur.start..prev.end];
- let (cur, prev) = cur_and_prev.split_at_mut(prev.start);
+ let (cur, prev) = cur_and_prev.split_at_mut(prev.start - cur.start);
let mut cur = Cursor::new(cur);
prev.chunks(block_size).for_each(|data| {
let h = hash_one_block(data, salt, block_size, algorithm);
@@ -200,7 +200,7 @@
#[test]
fn compare_with_golden_output() -> Result<()> {
// The golden outputs are generated by using the `fsverity` utility.
- let sizes = ["512", "4K", "1M", "10000000"];
+ let sizes = ["512", "4K", "1M", "10000000", "272629760"];
for size in sizes.iter() {
let input_name = format!("testdata/input.{}", size);
let mut input = File::open(&input_name)?;
diff --git a/idsig/testdata/create.sh b/idsig/testdata/create.sh
index eadfdb2..1a15d2b 100755
--- a/idsig/testdata/create.sh
+++ b/idsig/testdata/create.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-sizes="512 4K 1M 10000000"
+sizes="512 4K 1M 10000000 272629760"
for size in $sizes; do
echo $size
dd if=/dev/random of=input.$size bs=$size count=1
diff --git a/idsig/testdata/input.272629760 b/idsig/testdata/input.272629760
new file mode 100644
index 0000000..5bb6753
--- /dev/null
+++ b/idsig/testdata/input.272629760
Binary files differ
diff --git a/idsig/testdata/input.272629760.descriptor b/idsig/testdata/input.272629760.descriptor
new file mode 100644
index 0000000..70e0744
--- /dev/null
+++ b/idsig/testdata/input.272629760.descriptor
Binary files differ
diff --git a/idsig/testdata/input.272629760.hash b/idsig/testdata/input.272629760.hash
new file mode 100644
index 0000000..f2c68cc
--- /dev/null
+++ b/idsig/testdata/input.272629760.hash
Binary files differ
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index 8d9a7e3..c71d6ac 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -186,9 +186,11 @@
throws DeviceNotAvailableException {
CommandRunner android = new CommandRunner(androidDevice);
- // Install APK
- File apkFile = findTestFile(buildInfo, apkName);
- androidDevice.installPackage(apkFile, /* reinstall */ true);
+ // Install APK if necessary
+ if (apkName != null) {
+ File apkFile = findTestFile(buildInfo, apkName);
+ androidDevice.installPackage(apkFile, /* reinstall */ true);
+ }
// Get the path to the installed apk. Note that
// getDevice().getAppPackageInfo(...).getCodePath() doesn't work due to the incorrect