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()
+}