1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Support for generating and signing an info file listing names and digests of generated
18 //! artifacts.
19 
20 use crate::compos_key;
21 use crate::fsverity;
22 use anyhow::{anyhow, Context, Result};
23 use odsign_proto::odsign_info::OdsignInfo;
24 use protobuf::Message;
25 use std::fs::File;
26 use std::io::Write;
27 use std::os::unix::io::AsFd;
28 use std::path::Path;
29 
30 const TARGET_DIRECTORY: &str = "/data/misc/apexdata/com.android.art/dalvik-cache";
31 const SIGNATURE_EXTENSION: &str = ".signature";
32 
33 /// Accumulates and then signs information about generated artifacts.
34 pub struct ArtifactSigner<'a> {
35     base_directory: &'a Path,
36     file_digests: Vec<(String, String)>, // (File name, digest in hex)
37 }
38 
39 impl<'a> ArtifactSigner<'a> {
40     /// base_directory specifies the directory under which the artifacts are currently located;
41     /// they will eventually be moved under TARGET_DIRECTORY once they are verified and activated.
new(base_directory: &'a Path) -> Self42     pub fn new(base_directory: &'a Path) -> Self {
43         Self { base_directory, file_digests: Vec::new() }
44     }
45 
add_artifact(&mut self, path: &Path) -> Result<()>46     pub fn add_artifact(&mut self, path: &Path) -> Result<()> {
47         // The path we store is where the file will be when it is verified, not where it is now.
48         let suffix = path
49             .strip_prefix(self.base_directory)
50             .context("Artifacts must be under base directory")?;
51         let target_path = Path::new(TARGET_DIRECTORY).join(suffix);
52         let target_path = target_path.to_str().ok_or_else(|| anyhow!("Invalid path"))?;
53 
54         let file = File::open(path).with_context(|| format!("Opening {}", path.display()))?;
55         let digest = fsverity::measure(file.as_fd())?;
56         let digest = hex::encode(digest);
57 
58         self.file_digests.push((target_path.to_owned(), digest));
59         Ok(())
60     }
61 
62     /// Consume this ArtifactSigner and write details of all its artifacts to the given path,
63     /// with accompanying sigature file.
write_info_and_signature(self, info_path: &Path) -> Result<()>64     pub fn write_info_and_signature(self, info_path: &Path) -> Result<()> {
65         let mut info = OdsignInfo::new();
66         info.file_hashes.extend(self.file_digests);
67         let bytes = info.write_to_bytes()?;
68 
69         let signature = compos_key::sign(&bytes)?;
70 
71         let mut file =
72             File::create(info_path).with_context(|| format!("Creating {}", info_path.display()))?;
73         file.write_all(&bytes)?;
74 
75         let mut signature_name = info_path.file_name().unwrap().to_owned();
76         signature_name.push(SIGNATURE_EXTENSION);
77         let signature_path = info_path.with_file_name(&signature_name);
78         let mut signature_file = File::create(&signature_path)
79             .with_context(|| format!("Creating {}", signature_path.display()))?;
80         signature_file.write_all(&signature)?;
81 
82         Ok(())
83     }
84 }
85