Merge "Don't pass Tag::INCLUDE_UNIQUE_ID to attestKey"
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 02ef408..7713618 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -2114,11 +2114,12 @@
let tx = self.conn.unchecked_transaction().context(
"In retrieve_attestation_key_and_cert_chain: Failed to initialize transaction.",
)?;
- let key_id: i64;
- match self.query_kid_for_attestation_key_and_cert_chain(&tx, domain, namespace, km_uuid)? {
+ let key_id: i64 = match self
+ .query_kid_for_attestation_key_and_cert_chain(&tx, domain, namespace, km_uuid)?
+ {
None => return Ok(None),
- Some(kid) => key_id = kid,
- }
+ Some(kid) => kid,
+ };
tx.commit()
.context("In retrieve_attestation_key_and_cert_chain: Failed to commit keyid query")?;
let key_id_guard = KEY_ID_LOCK.get(key_id);
@@ -3667,13 +3668,13 @@
namespace_del1,
&KEYSTORE_UUID,
)?;
- assert!(!cert_chain.is_some());
+ assert!(cert_chain.is_none());
cert_chain = db.retrieve_attestation_key_and_cert_chain(
Domain::APP,
namespace_del2,
&KEYSTORE_UUID,
)?;
- assert!(!cert_chain.is_some());
+ assert!(cert_chain.is_none());
// Give the garbage collector half a second to catch up.
std::thread::sleep(Duration::from_millis(500));
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index 79504cf..192f445 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -1419,7 +1419,7 @@
setNumFreeSlots(15);
}
- softKeyMintDevice_.reset(CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE));
+ softKeyMintDevice_ = CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE);
}
sp<Keymaster> getDevice(KeyMintSecurityLevel securityLevel) {
@@ -1447,7 +1447,7 @@
static std::shared_ptr<IKeyMintDevice> swDevice;
std::lock_guard<std::mutex> lock(mutex);
if (!swDevice) {
- swDevice.reset(CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE));
+ swDevice = CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE);
}
return swDevice;
}
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 8abf2ba..13f7760 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -286,7 +286,7 @@
let operation = begin_result.operation.unwrap();
let update_aad_result = operation.updateAad(
- &b"foobar".to_vec(),
+ b"foobar".as_ref(),
None, /* authToken */
None, /* timestampToken */
);
@@ -310,7 +310,7 @@
let operation = begin_result.operation.unwrap();
let update_aad_result = operation.updateAad(
- &b"foobar".to_vec(),
+ b"foobar".as_ref(),
None, /* authToken */
None, /* timestampToken */
);
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 81fb06c..5a64020 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -932,8 +932,7 @@
for (uid, alias) in aliases
.into_iter()
- .map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
- .flatten()
+ .flat_map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
{
let (km_blob_params, _, _) = self
.legacy_loader
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 8574244..1f6be32 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -132,8 +132,7 @@
_ => Some(
certificate_chain
.iter()
- .map(|c| c.encodedCertificate.iter())
- .flatten()
+ .flat_map(|c| c.encodedCertificate.iter())
.copied()
.collect(),
),
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 376d44a..74e3e56 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -767,12 +767,8 @@
"Failed to super encrypt with LskfBound key."
)),
SuperEncryptionType::ScreenLockBound => {
- let entry = self
- .data
- .user_keys
- .get(&user_id)
- .map(|e| e.screen_lock_bound.as_ref())
- .flatten();
+ let entry =
+ self.data.user_keys.get(&user_id).and_then(|e| e.screen_lock_bound.as_ref());
if let Some(super_key) = entry {
Self::encrypt_with_aes_super_key(key_blob, super_key).context(concat!(
"In handle_super_encryption_on_key_init. ",
diff --git a/keystore2/test_utils/run_as.rs b/keystore2/test_utils/run_as.rs
index d42303d..2485ab5 100644
--- a/keystore2/test_utils/run_as.rs
+++ b/keystore2/test_utils/run_as.rs
@@ -30,9 +30,11 @@
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::{
close, fork, pipe as nix_pipe, read as nix_read, setgid, setuid, write as nix_write,
- ForkResult, Gid, Uid,
+ ForkResult, Gid, Pid, Uid,
};
use serde::{de::DeserializeOwned, Serialize};
+use std::io::{Read, Write};
+use std::marker::PhantomData;
use std::os::unix::io::RawFd;
fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
@@ -48,17 +50,10 @@
/// reads from the pipe into an expending vector, until no more data can be read.
struct PipeReader(RawFd);
-impl PipeReader {
- pub fn read_all(&self) -> Result<Vec<u8>, nix::Error> {
- let mut buffer = [0u8; 128];
- let mut result = Vec::<u8>::new();
- loop {
- let bytes = nix_read(self.0, &mut buffer)?;
- if bytes == 0 {
- return Ok(result);
- }
- result.extend_from_slice(&buffer[0..bytes]);
- }
+impl Read for PipeReader {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ let bytes = nix_read(self.0, buf)?;
+ Ok(bytes)
}
}
@@ -73,46 +68,264 @@
/// writes the given buffer into the pipe, returning the number of bytes written.
struct PipeWriter(RawFd);
-impl PipeWriter {
- pub fn write(&self, data: &[u8]) -> Result<usize, nix::Error> {
- nix_write(self.0, data)
- }
-}
-
impl Drop for PipeWriter {
fn drop(&mut self) {
close(self.0).expect("Failed to close writer pipe fd.");
}
}
+impl Write for PipeWriter {
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ let written = nix_write(self.0, buf)?;
+ Ok(written)
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ // Flush is a NO-OP.
+ Ok(())
+ }
+}
+
+/// Denotes the sender side of a serializing channel.
+pub struct ChannelWriter<T: Serialize + DeserializeOwned>(PipeWriter, PhantomData<T>);
+
+impl<T: Serialize + DeserializeOwned> ChannelWriter<T> {
+ /// Sends a serializable object to a the corresponding ChannelReader.
+ /// Sending is always non blocking. Panics if any error occurs during io or serialization.
+ pub fn send(&mut self, value: &T) {
+ let serialized = serde_cbor::to_vec(value)
+ .expect("In ChannelWriter::send: Failed to serialize to vector.");
+ let size = serialized.len().to_be_bytes();
+ match self.0.write(&size).expect("In ChannelWriter::send: Failed to write serialized size.")
+ {
+ w if w != std::mem::size_of::<usize>() => {
+ panic!(
+ "In ChannelWriter::send: Failed to write serialized size. (written: {}).",
+ w
+ );
+ }
+ _ => {}
+ };
+ match self
+ .0
+ .write(&serialized)
+ .expect("In ChannelWriter::send: Failed to write serialized data.")
+ {
+ w if w != serialized.len() => {
+ panic!(
+ "In ChannelWriter::send: Failed to write serialized data. (written: {}).",
+ w
+ );
+ }
+ _ => {}
+ };
+ }
+}
+
+/// Represents the receiving and of a serializing channel.
+pub struct ChannelReader<T>(PipeReader, PhantomData<T>);
+
+impl<T: Serialize + DeserializeOwned> ChannelReader<T> {
+ /// Receives a serializable object from the corresponding ChannelWriter.
+ /// Receiving blocks until an object of type T has been read from the channel.
+ /// Panics if an error occurs during io or deserialization.
+ pub fn recv(&mut self) -> T {
+ let mut size_buffer = [0u8; std::mem::size_of::<usize>()];
+ match self.0.read(&mut size_buffer).expect("In ChannelReader::recv: Failed to read size.") {
+ r if r != size_buffer.len() => {
+ panic!("In ChannelReader::recv: Failed to read size. Insufficient data: {}", r);
+ }
+ _ => {}
+ };
+ let size = usize::from_be_bytes(size_buffer);
+ let mut data_buffer = vec![0u8; size];
+ match self
+ .0
+ .read(&mut data_buffer)
+ .expect("In ChannelReader::recv: Failed to read serialized data.")
+ {
+ r if r != data_buffer.len() => {
+ panic!(
+ "In ChannelReader::recv: Failed to read serialized data. Insufficient data: {}",
+ r
+ );
+ }
+ _ => {}
+ };
+
+ serde_cbor::from_slice(&data_buffer)
+ .expect("In ChannelReader::recv: Failed to deserialize data.")
+ }
+}
+
fn pipe() -> Result<(PipeReader, PipeWriter), nix::Error> {
let (read_fd, write_fd) = nix_pipe()?;
Ok((PipeReader(read_fd), PipeWriter(write_fd)))
}
+fn pipe_channel<T>() -> Result<(ChannelReader<T>, ChannelWriter<T>), nix::Error>
+where
+ T: Serialize + DeserializeOwned,
+{
+ let (reader, writer) = pipe()?;
+ Ok((
+ ChannelReader::<T>(reader, Default::default()),
+ ChannelWriter::<T>(writer, Default::default()),
+ ))
+}
+
+/// Handle for handling child processes.
+pub struct ChildHandle<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> {
+ pid: Pid,
+ result_reader: ChannelReader<R>,
+ cmd_writer: ChannelWriter<M>,
+ response_reader: ChannelReader<M>,
+ exit_status: Option<WaitStatus>,
+}
+
+impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> ChildHandle<R, M> {
+ /// Send a command message to the child.
+ pub fn send(&mut self, data: &M) {
+ self.cmd_writer.send(data)
+ }
+
+ /// Receive a response from the child.
+ pub fn recv(&mut self) -> M {
+ self.response_reader.recv()
+ }
+
+ /// Get child result. Panics if the child did not exit with status 0 or if a serialization
+ /// error occurred.
+ pub fn get_result(mut self) -> R {
+ let status =
+ waitpid(self.pid, None).expect("ChildHandle::wait: Failed while waiting for child.");
+ match status {
+ WaitStatus::Exited(pid, 0) => {
+ // Child exited successfully.
+ // Read the result from the pipe.
+ self.exit_status = Some(WaitStatus::Exited(pid, 0));
+ self.result_reader.recv()
+ }
+ WaitStatus::Exited(pid, c) => {
+ panic!("Child did not exit as expected: {:?}", WaitStatus::Exited(pid, c));
+ }
+ status => {
+ panic!("Child did not exit at all: {:?}", status);
+ }
+ }
+ }
+}
+
+impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> Drop for ChildHandle<R, M> {
+ fn drop(&mut self) {
+ if self.exit_status.is_none() {
+ panic!("Child result not checked.")
+ }
+ }
+}
+
+/// Run the given closure in a new process running with the new identity given as
+/// `uid`, `gid`, and `se_context`. Parent process will run without waiting for child status.
+///
+/// # Safety
+/// run_as_child runs the given closure in the client branch of fork. And it uses non
+/// async signal safe API. This means that calling this function in a multi threaded program
+/// yields undefined behavior in the child. As of this writing, it is safe to call this function
+/// from a Rust device test, because every test itself is spawned as a separate process.
+///
+/// # Safety Binder
+/// It is okay for the closure to use binder services, however, this does not work
+/// if the parent initialized libbinder already. So do not use binder outside of the closure
+/// in your test.
+pub unsafe fn run_as_child<F, R, M>(
+ se_context: &str,
+ uid: Uid,
+ gid: Gid,
+ f: F,
+) -> Result<ChildHandle<R, M>, nix::Error>
+where
+ R: Serialize + DeserializeOwned,
+ M: Serialize + DeserializeOwned,
+ F: 'static + Send + FnOnce(&mut ChannelReader<M>, &mut ChannelWriter<M>) -> R,
+{
+ let se_context =
+ selinux::Context::new(se_context).expect("Unable to construct selinux::Context.");
+ let (result_reader, mut result_writer) = pipe_channel().expect("Failed to create pipe.");
+ let (mut cmd_reader, cmd_writer) = pipe_channel().expect("Failed to create cmd pipe.");
+ let (response_reader, mut response_writer) =
+ pipe_channel().expect("Failed to create cmd pipe.");
+
+ match fork() {
+ Ok(ForkResult::Parent { child, .. }) => {
+ drop(response_writer);
+ drop(cmd_reader);
+ drop(result_writer);
+
+ Ok(ChildHandle::<R, M> {
+ pid: child,
+ result_reader,
+ response_reader,
+ cmd_writer,
+ exit_status: None,
+ })
+ }
+ Ok(ForkResult::Child) => {
+ drop(cmd_writer);
+ drop(response_reader);
+ drop(result_reader);
+
+ // This will panic on error or insufficient privileges.
+ transition(se_context, uid, gid);
+
+ // Run the closure.
+ let result = f(&mut cmd_reader, &mut response_writer);
+
+ // Serialize the result of the closure.
+ result_writer.send(&result);
+
+ // Set exit status to `0`.
+ std::process::exit(0);
+ }
+ Err(errno) => {
+ panic!("Failed to fork: {:?}", errno);
+ }
+ }
+}
+
/// Run the given closure in a new process running with the new identity given as
/// `uid`, `gid`, and `se_context`.
-pub fn run_as<F, R>(se_context: &str, uid: Uid, gid: Gid, f: F) -> R
+///
+/// # Safety
+/// run_as runs the given closure in the client branch of fork. And it uses non
+/// async signal safe API. This means that calling this function in a multi threaded program
+/// yields undefined behavior in the child. As of this writing, it is safe to call this function
+/// from a Rust device test, because every test itself is spawned as a separate process.
+///
+/// # Safety Binder
+/// It is okay for the closure to use binder services, however, this does not work
+/// if the parent initialized libbinder already. So do not use binder outside of the closure
+/// in your test.
+pub unsafe fn run_as<F, R>(se_context: &str, uid: Uid, gid: Gid, f: F) -> R
where
R: Serialize + DeserializeOwned,
F: 'static + Send + FnOnce() -> R,
{
let se_context =
selinux::Context::new(se_context).expect("Unable to construct selinux::Context.");
- let (reader, writer) = pipe().expect("Failed to create pipe.");
+ let (mut reader, mut writer) = pipe_channel::<R>().expect("Failed to create pipe.");
- match unsafe { fork() } {
+ match fork() {
Ok(ForkResult::Parent { child, .. }) => {
drop(writer);
let status = waitpid(child, None).expect("Failed while waiting for child.");
if let WaitStatus::Exited(_, 0) = status {
// Child exited successfully.
// Read the result from the pipe.
- let serialized_result =
- reader.read_all().expect("Failed to read result from child.");
+ // let serialized_result =
+ // reader.read_all().expect("Failed to read result from child.");
// Deserialize the result and return it.
- serde_cbor::from_slice(&serialized_result).expect("Failed to deserialize result.")
+ reader.recv()
} else {
panic!("Child did not exit as expected {:?}", status);
}
@@ -125,10 +338,7 @@
let result = f();
// Serialize the result of the closure.
- let vec = serde_cbor::to_vec(&result).expect("Result serialization failed");
-
- // Send the result to the parent using the pipe.
- writer.write(&vec).expect("Failed to send serialized result to parent.");
+ writer.send(&result);
// Set exit status to `0`.
std::process::exit(0);
@@ -151,9 +361,13 @@
#[test]
#[should_panic]
fn test_run_as_panics_on_closure_panic() {
- run_as(selinux::getcon().unwrap().to_str().unwrap(), getuid(), getgid(), || {
- panic!("Closure must panic.")
- });
+ // Safety: run_as must be called from a single threaded process.
+ // This device test is run as a separate single threaded process.
+ unsafe {
+ run_as(selinux::getcon().unwrap().to_str().unwrap(), getuid(), getgid(), || {
+ panic!("Closure must panic.")
+ })
+ };
}
static TARGET_UID: Uid = Uid::from_raw(10020);
@@ -163,11 +377,15 @@
/// Tests that the closure is running as the target identity.
#[test]
fn test_transition_to_untrusted_app() {
- run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || {
- assert_eq!(TARGET_UID, getuid());
- assert_eq!(TARGET_GID, getgid());
- assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
- });
+ // Safety: run_as must be called from a single threaded process.
+ // This device test is run as a separate single threaded process.
+ unsafe {
+ run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || {
+ assert_eq!(TARGET_UID, getuid());
+ assert_eq!(TARGET_GID, getgid());
+ assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
+ })
+ };
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@@ -185,7 +403,72 @@
c: "supercalifragilisticexpialidocious".to_owned(),
};
let test_result_clone = test_result.clone();
- let result = run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || test_result_clone);
+ // Safety: run_as must be called from a single threaded process.
+ // This device test is run as a separate single threaded process.
+ let result = unsafe { run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || test_result_clone) };
assert_eq!(test_result, result);
}
+
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+ enum PingPong {
+ Ping,
+ Pong,
+ }
+
+ /// Tests that closure is running under given user identity and communicates with calling
+ /// process using pipe.
+ #[test]
+ fn test_run_as_child() {
+ let test_result = SomeResult {
+ a: 5,
+ b: 0xffffffffffffffff,
+ c: "supercalifragilisticexpialidocious".to_owned(),
+ };
+ let test_result_clone = test_result.clone();
+
+ // Safety: run_as_child must be called from a single threaded process.
+ // This device test is run as a separate single threaded process.
+ let mut child_handle: ChildHandle<SomeResult, PingPong> = unsafe {
+ run_as_child(TARGET_CTX, TARGET_UID, TARGET_GID, |cmd_reader, response_writer| {
+ assert_eq!(TARGET_UID, getuid());
+ assert_eq!(TARGET_GID, getgid());
+ assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
+
+ let ping: PingPong = cmd_reader.recv();
+ assert_eq!(ping, PingPong::Ping);
+
+ response_writer.send(&PingPong::Pong);
+
+ let ping: PingPong = cmd_reader.recv();
+ assert_eq!(ping, PingPong::Ping);
+ let pong: PingPong = cmd_reader.recv();
+ assert_eq!(pong, PingPong::Pong);
+
+ response_writer.send(&PingPong::Pong);
+ response_writer.send(&PingPong::Ping);
+
+ test_result_clone
+ })
+ .unwrap()
+ };
+
+ // Send one ping.
+ child_handle.send(&PingPong::Ping);
+
+ // Expect one pong.
+ let pong = child_handle.recv();
+ assert_eq!(pong, PingPong::Pong);
+
+ // Send ping and pong.
+ child_handle.send(&PingPong::Ping);
+ child_handle.send(&PingPong::Pong);
+
+ // Expect pong and ping.
+ let pong = child_handle.recv();
+ assert_eq!(pong, PingPong::Pong);
+ let ping = child_handle.recv();
+ assert_eq!(ping, PingPong::Ping);
+
+ assert_eq!(child_handle.get_result(), test_result);
+ }
}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 8ea0727..cd9a1ea 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -26,6 +26,7 @@
#include <sys/types.h>
#include <sys/wait.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <asm/byteorder.h>
@@ -125,6 +126,19 @@
}
}
};
+
+static Result<void> measureFsVerity(int fd, const fsverity_digest* digest) {
+ if (ioctl(fd, FS_IOC_MEASURE_VERITY, digest) != 0) {
+ if (errno == ENODATA) {
+ return Error() << "File is not in fs-verity";
+ } else {
+ return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY";
+ }
+ }
+
+ return {};
+}
+
} // namespace
template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>;
@@ -198,14 +212,12 @@
static Result<std::string> isFileInVerity(int fd) {
auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
- auto ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
- if (ret < 0) {
- if (errno == ENODATA) {
- return Error() << "File is not in fs-verity";
- } else {
- return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY";
- }
+
+ const auto& status = measureFsVerity(fd, d.get());
+ if (!status.ok()) {
+ return status.error();
}
+
return toHex({&d->digest[0], &d->digest[d->digest_size]});
}
@@ -256,6 +268,31 @@
return digests;
}
+Result<void> enableFsVerity(const std::string& path, const std::string& signature_path) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (!fd.ok()) {
+ return Error() << "Can't open " << path;
+ }
+
+ std::string signature;
+ android::base::ReadFileToString(signature_path, &signature);
+ std::vector<uint8_t> span = std::vector<uint8_t>(signature.begin(), signature.end());
+
+ const auto& enable = enableFsVerity(fd.get(), span);
+ if (!enable.ok()) {
+ return enable.error();
+ }
+
+ auto digest = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+ digest->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
+ const auto& measure = measureFsVerity(fd.get(), digest.get());
+ if (!measure.ok()) {
+ return measure.error();
+ }
+
+ return {};
+}
+
Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
std::map<std::string, std::string> digests;
std::error_code ec;
diff --git a/ondevice-signing/include/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
index 0650563..e6e49c7 100644
--- a/ondevice-signing/include/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -36,6 +36,10 @@
android::base::Result<std::map<std::string, std::string>>
addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
+// Enable verity on the provided file, using the given PKCS7 signature.
+android::base::Result<void> enableFsVerity(const std::string& path,
+ const std::string& signature_path);
+
android::base::Result<void>
verifyAllFilesUsingCompOs(const std::string& directory_path,
const std::map<std::string, std::string>& digests,
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 7be8b51..fc55846 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -205,7 +205,7 @@
for (const auto& path_digest : digests) {
auto path = path_digest.first;
auto digest = path_digest.second;
- if ((trusted_digests.count(path) == 0)) {
+ if (trusted_digests.count(path) == 0) {
return Error() << "Couldn't find digest for " << path;
}
if (trusted_digests.at(path) != digest) {
@@ -240,7 +240,7 @@
return verifyDigests(*result, trusted_digests);
}
-Result<OdsignInfo> getOdsignInfo(const SigningKey& key) {
+Result<OdsignInfo> getAndVerifyOdsignInfo(const SigningKey& key) {
std::string persistedSignature;
OdsignInfo odsignInfo;
@@ -274,6 +274,28 @@
return odsignInfo;
}
+std::map<std::string, std::string> getTrustedDigests(const SigningKey& key) {
+ std::map<std::string, std::string> trusted_digests;
+
+ if (access(kOdsignInfo.c_str(), F_OK) != 0) {
+ // no odsign info file, which is not necessarily an error - just return
+ // an empty list of digests.
+ LOG(INFO) << kOdsignInfo << " not found.";
+ return trusted_digests;
+ }
+ auto signInfo = getAndVerifyOdsignInfo(key);
+
+ if (signInfo.ok()) {
+ trusted_digests.insert(signInfo->file_hashes().begin(), signInfo->file_hashes().end());
+ } else {
+ // This is not expected, since the file did exist. Log an error and
+ // return an empty list of digests.
+ LOG(ERROR) << "Couldn't load trusted digests: " << signInfo.error();
+ }
+
+ return trusted_digests;
+}
+
Result<void> persistDigests(const std::map<std::string, std::string>& digests,
const SigningKey& key) {
OdsignInfo signInfo;
@@ -299,23 +321,8 @@
return {};
}
-Result<void> verifyArtifacts(const SigningKey& key, bool supportsFsVerity) {
- auto signInfo = getOdsignInfo(key);
- // Tell init we're done with the key; this is a boot time optimization
- // in particular for the no fs-verity case, where we need to do a
- // costly verification. If the files haven't been tampered with, which
- // should be the common path, the verification will succeed, and we won't
- // need the key anymore. If it turns out the artifacts are invalid (eg not
- // in fs-verity) or the hash doesn't match, we won't be able to generate
- // new artifacts without the key, so in those cases, remove the artifacts,
- // and use JIT zygote for the current boot. We should recover automatically
- // by the next boot.
- SetProperty(kOdsignKeyDoneProp, "1");
- if (!signInfo.ok()) {
- return signInfo.error();
- }
- std::map<std::string, std::string> trusted_digests(signInfo->file_hashes().begin(),
- signInfo->file_hashes().end());
+Result<void> verifyArtifactsIntegrity(const std::map<std::string, std::string>& trusted_digests,
+ bool supportsFsVerity) {
Result<void> integrityStatus;
if (supportsFsVerity) {
@@ -361,7 +368,8 @@
art::odrefresh::ExitCode checkCompOsPendingArtifacts(const SigningKey& signing_key,
bool* digests_verified) {
if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
- return art::odrefresh::ExitCode::kCompilationRequired;
+ // No pending CompOS artifacts, all that matters is the current ones.
+ return checkArtifacts();
}
// CompOS has generated some artifacts that may, or may not, match the
@@ -406,9 +414,17 @@
} else {
LOG(INFO) << "CompOS artifacts successfully verified.";
odrefresh_status = checkArtifacts();
- if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
- // We have digests of all the files, and they aren't going to change, so
- // we can just sign them & save them now, and skip checking them later.
+ switch (odrefresh_status) {
+ case art::odrefresh::ExitCode::kCompilationRequired:
+ // We have verified all the files, and we need to make sure
+ // we don't check them against odsign.info which will be out
+ // of date.
+ *digests_verified = true;
+ return odrefresh_status;
+ case art::odrefresh::ExitCode::kOkay: {
+ // We have digests of all the files, so we can just sign them & save them now.
+ // We need to make sure we don't check them against odsign.info which will
+ // be out of date.
auto persisted = persistDigests(compos_digests, signing_key);
if (!persisted.ok()) {
LOG(ERROR) << persisted.error();
@@ -418,8 +434,11 @@
}
LOG(INFO) << "Persisted CompOS digests.";
*digests_verified = true;
+ return odrefresh_status;
}
- return odrefresh_status;
+ default:
+ return odrefresh_status;
+ }
}
}
@@ -495,33 +514,60 @@
}
}
- art::odrefresh::ExitCode odrefresh_status = art::odrefresh::ExitCode::kCompilationRequired;
bool digests_verified = false;
+ art::odrefresh::ExitCode odrefresh_status =
+ useCompOs ? checkCompOsPendingArtifacts(*key, &digests_verified) : checkArtifacts();
- if (useCompOs) {
- odrefresh_status = checkCompOsPendingArtifacts(*key, &digests_verified);
+ // The artifacts dir doesn't necessarily need to exist; if the existing
+ // artifacts on the system partition are valid, those can be used.
+ int err = access(kArtArtifactsDir.c_str(), F_OK);
+ // If we receive any error other than ENOENT, be suspicious
+ bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
+
+ if (artifactsPresent && !digests_verified &&
+ (odrefresh_status == art::odrefresh::ExitCode::kOkay ||
+ odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired)) {
+ // If we haven't verified the digests yet, we need to validate them. We
+ // need to do this both in case the existing artifacts are okay, but
+ // also if odrefresh said that a recompile is required. In the latter
+ // case, odrefresh may use partial compilation, and leave some
+ // artifacts unchanged.
+ auto trusted_digests = getTrustedDigests(*key);
+
+ if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+ // Tell init we're done with the key; this is a boot time optimization
+ // in particular for the no fs-verity case, where we need to do a
+ // costly verification. If the files haven't been tampered with, which
+ // should be the common path, the verification will succeed, and we won't
+ // need the key anymore. If it turns out the artifacts are invalid (eg not
+ // in fs-verity) or the hash doesn't match, we won't be able to generate
+ // new artifacts without the key, so in those cases, remove the artifacts,
+ // and use JIT zygote for the current boot. We should recover automatically
+ // by the next boot.
+ SetProperty(kOdsignKeyDoneProp, "1");
+ }
+
+ auto verificationResult = verifyArtifactsIntegrity(trusted_digests, supportsFsVerity);
+ if (!verificationResult.ok()) {
+ int num_removed = removeDirectory(kArtArtifactsDir);
+ if (num_removed == 0) {
+ // If we can't remove the bad artifacts, we shouldn't continue, and
+ // instead prevent Zygote from using them (which is taken care of
+ // in the exit handler).
+ LOG(ERROR) << "Failed to remove unknown artifacts.";
+ return -1;
+ }
+ }
}
+ // Now that we verified existing artifacts, compile if we need to.
if (odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired) {
odrefresh_status = compileArtifacts(kForceCompilation);
}
+
if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+ // No new artifacts generated, and we verified existing ones above, nothing left to do.
LOG(INFO) << "odrefresh said artifacts are VALID";
- if (!digests_verified) {
- // A post-condition of validating artifacts is that if the ones on /system
- // are used, kArtArtifactsDir is removed. Conversely, if kArtArtifactsDir
- // exists, those are artifacts that will be used, and we should verify them.
- int err = access(kArtArtifactsDir.c_str(), F_OK);
- // If we receive any error other than ENOENT, be suspicious
- bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
- if (artifactsPresent) {
- auto verificationResult = verifyArtifacts(*key, supportsFsVerity);
- if (!verificationResult.ok()) {
- LOG(ERROR) << verificationResult.error();
- return -1;
- }
- }
- }
} else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
odrefresh_status == art::odrefresh::ExitCode::kCompilationFailed) {
const bool compiled_all = odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess;
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index aac4878..665a9e7 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -47,8 +47,10 @@
name: "rkp_factory_extraction_tool",
vendor: true,
srcs: ["rkp_factory_extraction_tool.cpp"],
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
shared_libs: [
- "android.hardware.security.keymint-V1-ndk",
"libbinder",
"libbinder_ndk",
"libcrypto",
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 9786c3d..0f45531 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -30,6 +30,7 @@
using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
using aidl::android::hardware::security::keymint::MacedPublicKey;
using aidl::android::hardware::security::keymint::ProtectedData;
+using aidl::android::hardware::security::keymint::RpcHardwareInfo;
using aidl::android::hardware::security::keymint::remote_prov::generateEekChain;
using aidl::android::hardware::security::keymint::remote_prov::getProdEekChain;
using aidl::android::hardware::security::keymint::remote_prov::jsonEncodeCsrWithBuild;
@@ -113,10 +114,10 @@
return certificateRequest;
}
-std::vector<uint8_t> getEekChain() {
+std::vector<uint8_t> getEekChain(uint32_t curve) {
if (FLAGS_test_mode) {
const std::vector<uint8_t> kFakeEekId = {'f', 'a', 'k', 'e', 0};
- auto eekOrErr = generateEekChain(3 /* chainlength */, kFakeEekId);
+ auto eekOrErr = generateEekChain(curve, 3 /* chainlength */, kFakeEekId);
if (!eekOrErr) {
std::cerr << "Failed to generate test EEK somehow: " << eekOrErr.message() << std::endl;
exit(-1);
@@ -128,15 +129,15 @@
return eek;
}
- return getProdEekChain();
+ return getProdEekChain(curve);
}
-void writeOutput(const Array& csr) {
+void writeOutput(const std::string instance_name, const Array& csr) {
if (FLAGS_output_format == kBinaryCsrOutput) {
auto bytes = csr.encode();
std::copy(bytes.begin(), bytes.end(), std::ostream_iterator<char>(std::cout));
} else if (FLAGS_output_format == kBuildPlusCsr) {
- auto [json, error] = jsonEncodeCsrWithBuild(csr);
+ auto [json, error] = jsonEncodeCsrWithBuild(instance_name, csr);
if (!error.empty()) {
std::cerr << "Error JSON encoding the output: " << error;
exit(1);
@@ -169,9 +170,16 @@
std::vector<MacedPublicKey> emptyKeys;
DeviceInfo verifiedDeviceInfo;
ProtectedData protectedData;
- ::ndk::ScopedAStatus status = rkp_service->generateCertificateRequest(
- FLAGS_test_mode, emptyKeys, getEekChain(), challenge, &verifiedDeviceInfo, &protectedData,
- &keysToSignMac);
+ RpcHardwareInfo hwInfo;
+ ::ndk::ScopedAStatus status = rkp_service->getHardwareInfo(&hwInfo);
+ if (!status.isOk()) {
+ std::cerr << "Failed to get hardware info for '" << fullName
+ << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ exit(-1);
+ }
+ status = rkp_service->generateCertificateRequest(
+ FLAGS_test_mode, emptyKeys, getEekChain(hwInfo.supportedEekCurve), challenge,
+ &verifiedDeviceInfo, &protectedData, &keysToSignMac);
if (!status.isOk()) {
std::cerr << "Bundle extraction failed for '" << fullName
<< "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
@@ -179,7 +187,7 @@
}
auto request =
composeCertificateRequest(protectedData, verifiedDeviceInfo, challenge, keysToSignMac);
- writeOutput(request);
+ writeOutput(std::string(name), request);
}
} // namespace