Add code to bulk-sign artifacts
This isn't wired up yet - we need to be able to iterate over AuthFs
directories, but that's coming soon.
Import the OdsignInfo proto, which contains the map of filenames and
digests, and provide a way to build it from a set of artifact files
and write it along with its signature.
Also remove the old Signer trait (which is unused), and a couple of
small refactorings.
Bug: 161471326
Test: Builds
Change-Id: I37fccb1f2ca4fa1ea3006185d9f805d668252e2a
diff --git a/compos/src/artifact_signer.rs b/compos/src/artifact_signer.rs
new file mode 100644
index 0000000..54b8d7a
--- /dev/null
+++ b/compos/src/artifact_signer.rs
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+//! Support for generating and signing an info file listing names and digests of generated
+//! artifacts.
+
+#![allow(dead_code)] // Will be used soon
+
+use crate::compos_key_service::CompOsKeyService;
+use crate::fsverity;
+use anyhow::{anyhow, Context, Result};
+use odsign_proto::odsign_info::OdsignInfo;
+use protobuf::Message;
+use std::fs::File;
+use std::io::Write;
+use std::os::unix::io::AsRawFd;
+use std::path::{Path, PathBuf};
+
+const TARGET_DIRECTORY: &str = "/data/misc/apexdata/com.android.art/dalvik-cache";
+const SIGNATURE_EXTENSION: &str = ".signature";
+
+/// Accumulates and then signs information about generated artifacts.
+pub struct ArtifactSigner<'a> {
+ key_blob: Vec<u8>,
+ key_service: &'a CompOsKeyService,
+ base_directory: PathBuf,
+ file_digests: Vec<(String, String)>, // (File name, digest in hex)
+}
+
+impl<'a> ArtifactSigner<'a> {
+ /// base_directory specifies the directory under which the artifacts are currently located;
+ /// they will eventually be moved under TARGET_DIRECTORY once they are verified and activated.
+ pub fn new(
+ key_blob: Vec<u8>,
+ key_service: &'a CompOsKeyService,
+ base_directory: PathBuf,
+ ) -> Self {
+ ArtifactSigner { key_blob, key_service, base_directory, file_digests: Vec::new() }
+ }
+
+ pub fn add_artifact(&mut self, path: PathBuf) -> Result<()> {
+ // The path we store is where the file will be when it is verified, not where it is now.
+ let suffix = path
+ .strip_prefix(&self.base_directory)
+ .context("Artifacts must be under base directory")?;
+ let target_path = Path::new(TARGET_DIRECTORY).join(suffix);
+ let target_path = target_path.to_str().ok_or_else(|| anyhow!("Invalid path"))?;
+
+ let file = File::open(&path)?;
+ let digest = fsverity::measure(file.as_raw_fd())?;
+ let digest = to_hex_string(&digest);
+
+ self.file_digests.push((target_path.to_owned(), digest));
+ Ok(())
+ }
+
+ /// Consume this ArtifactSigner and write details of all its artifacts to the given path,
+ /// with accompanying sigature file.
+ pub fn write_info_and_signature(self, info_path: &Path) -> Result<()> {
+ let mut info = OdsignInfo::new();
+ info.mut_file_hashes().extend(self.file_digests.into_iter());
+ let bytes = info.write_to_bytes()?;
+
+ let signature = self.key_service.sign(&self.key_blob, &bytes)?;
+
+ let mut file = File::create(info_path)?;
+ file.write_all(&bytes)?;
+
+ let mut signature_name = info_path.file_name().unwrap().to_owned();
+ signature_name.push(SIGNATURE_EXTENSION);
+ let signature_path = info_path.with_file_name(&signature_name);
+ let mut signature_file = File::create(&signature_path)?;
+ signature_file.write_all(&signature)?;
+
+ Ok(())
+ }
+}
+
+fn to_hex_string(buf: &[u8]) -> String {
+ buf.iter().map(|b| format!("{:02x}", b)).collect()
+}