blob: b5eb8cbe05f9daa7accb69a45883ba0d829b8823 [file] [log] [blame]
/*
* 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::fsverity;
use crate::signing_key::DiceSigner;
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;
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_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, signer: DiceSigner, 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 = signer.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(())
}
}
fn to_hex_string(buf: &[u8]) -> String {
buf.iter().map(|b| format!("{:02x}", b)).collect()
}