| /* |
| * 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. |
| |
| use crate::compos_key; |
| 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::AsFd; |
| use std::path::Path; |
| |
| 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> { |
| base_directory: &'a Path, |
| 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(base_directory: &'a Path) -> Self { |
| Self { base_directory, file_digests: Vec::new() } |
| } |
| |
| pub fn add_artifact(&mut self, path: &Path) -> 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).with_context(|| format!("Opening {}", path.display()))?; |
| let digest = fsverity::measure(file.as_fd())?; |
| let digest = hex::encode(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.file_hashes.extend(self.file_digests); |
| let bytes = info.write_to_bytes()?; |
| |
| let signature = compos_key::sign(&bytes)?; |
| |
| let mut file = |
| File::create(info_path).with_context(|| format!("Creating {}", info_path.display()))?; |
| 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) |
| .with_context(|| format!("Creating {}", signature_path.display()))?; |
| signature_file.write_all(&signature)?; |
| |
| Ok(()) |
| } |
| } |