forked from containers/bootc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import fsverity ioctl bits from composefs-rs
Imported from https://github.com/containers/composefs-rs until we take a dependency on it. Signed-off-by: Colin Walters <[email protected]>
- Loading branch information
1 parent
c4f3e33
commit c534a1e
Showing
3 changed files
with
268 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
use std::cmp::min; | ||
|
||
use sha2::{Digest, Sha256}; | ||
|
||
use super::Sha256HashValue; | ||
|
||
// TODO: support Sha512 | ||
|
||
struct FsVerityLayer { | ||
context: Sha256, | ||
remaining: usize, | ||
} | ||
|
||
impl FsVerityLayer { | ||
fn new() -> FsVerityLayer { | ||
FsVerityLayer { | ||
context: Sha256::new(), | ||
remaining: 4096, | ||
} | ||
} | ||
|
||
fn add_data(&mut self, data: &[u8]) { | ||
self.context.update(data); | ||
self.remaining -= data.len(); | ||
} | ||
|
||
fn complete(&mut self) -> Sha256HashValue { | ||
self.context.update(&[0u8; 4096][..self.remaining]); | ||
self.remaining = 4096; | ||
self.context.finalize_reset().into() | ||
} | ||
} | ||
|
||
pub struct FsVerityHasher { | ||
layers: Vec<FsVerityLayer>, | ||
value: Option<Sha256HashValue>, | ||
n_bytes: u64, | ||
} | ||
|
||
impl FsVerityHasher { | ||
pub fn hash(buffer: &[u8]) -> Sha256HashValue { | ||
let mut hasher = FsVerityHasher::new(); | ||
|
||
let mut start = 0; | ||
while start < buffer.len() { | ||
let end = min(start + 4096, buffer.len()); | ||
hasher.add_data(&buffer[start..end]); | ||
start = end; | ||
} | ||
|
||
hasher.digest() | ||
} | ||
|
||
pub fn new() -> FsVerityHasher { | ||
FsVerityHasher { | ||
layers: vec![], | ||
value: None, | ||
n_bytes: 0, | ||
} | ||
} | ||
|
||
pub fn add_data(&mut self, data: &[u8]) { | ||
if let Some(value) = self.value { | ||
// We had a complete value, but now we're adding new data. | ||
// This means that we need to add a new hash layer... | ||
let mut new_layer = FsVerityLayer::new(); | ||
new_layer.add_data(&value); | ||
self.layers.push(new_layer); | ||
self.value = None; | ||
} | ||
|
||
// Get the value of this block | ||
let mut context = FsVerityLayer::new(); | ||
context.add_data(data); | ||
let mut value = context.complete(); | ||
self.n_bytes += data.len() as u64; | ||
|
||
for layer in self.layers.iter_mut() { | ||
// We have a layer we need to hash this value into | ||
layer.add_data(&value); | ||
if layer.remaining != 0 { | ||
return; | ||
} | ||
// ...but now this layer itself is now complete, so get the value of *it*. | ||
value = layer.complete(); | ||
} | ||
|
||
// If we made it this far, we completed the last layer and have a value. Store it. | ||
self.value = Some(value); | ||
} | ||
|
||
pub fn root_hash(&mut self) -> Sha256HashValue { | ||
if let Some(value) = self.value { | ||
value | ||
} else { | ||
let mut value = [0u8; 32]; | ||
|
||
for layer in self.layers.iter_mut() { | ||
// We have a layer we need to hash this value into | ||
if value != [0u8; 32] { | ||
layer.add_data(&value); | ||
} | ||
if layer.remaining != 4096 { | ||
// ...but now this layer itself is complete, so get the value of *it*. | ||
value = layer.complete(); | ||
} else { | ||
value = [0u8; 32]; | ||
} | ||
} | ||
|
||
self.value = Some(value); | ||
|
||
value | ||
} | ||
} | ||
|
||
pub fn digest(&mut self) -> Sha256HashValue { | ||
/* | ||
let descriptor = FsVerityDescriptor { | ||
version: 1, | ||
hash_algorithm: 1, | ||
log_blocksize: 12, | ||
salt_size: 0, | ||
reserved_0x04: 0, | ||
data_size: self.n_bytes, | ||
root_hash: (self.root_hash(), [0; 32]), | ||
salt: [0; 32], | ||
reserved: [0; 144], | ||
}; | ||
let mut context = Sha256::new(); | ||
context.update(descriptor); | ||
return context.finalize().into(); | ||
*/ | ||
|
||
let mut context = Sha256::new(); | ||
context.update(1u8.to_le_bytes()); /* version */ | ||
context.update(1u8.to_le_bytes()); /* hash_algorithm */ | ||
context.update(12u8.to_le_bytes()); /* log_blocksize */ | ||
context.update(0u8.to_le_bytes()); /* salt_size */ | ||
context.update([0; 4]); /* reserved */ | ||
context.update(self.n_bytes.to_le_bytes()); | ||
context.update(self.root_hash()); | ||
context.update([0; 32]); | ||
context.update([0; 32]); /* salt */ | ||
context.update([0; 144]); /* reserved */ | ||
context.finalize().into() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use anyhow::Result; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_digest() -> Result<()> { | ||
let digest = FsVerityHasher::hash(b"hello world"); | ||
assert_eq!( | ||
digest, | ||
[ | ||
30, 46, 170, 66, 2, 215, 80, 164, 17, 116, 238, 69, 73, 112, 185, 44, 27, 194, 249, | ||
37, 177, 227, 80, 118, 216, 199, 213, 245, 99, 98, 186, 100 | ||
] | ||
); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
use std::os::fd::AsFd; | ||
|
||
use anyhow::Result; | ||
use rustix::ioctl; | ||
|
||
use super::FsVerityHashValue; | ||
|
||
// See /usr/include/linux/fsverity.h | ||
#[repr(C)] | ||
pub struct FsVerityEnableArg { | ||
version: u32, | ||
hash_algorithm: u32, | ||
block_size: u32, | ||
salt_size: u32, | ||
salt_ptr: u64, | ||
sig_size: u32, | ||
__reserved1: u32, | ||
sig_ptr: u64, | ||
__reserved2: [u64; 11], | ||
} | ||
|
||
// #define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg) | ||
type FsIocEnableVerity = ioctl::WriteOpcode<b'f', 133, FsVerityEnableArg>; | ||
|
||
pub fn fs_ioc_enable_verity<F: AsFd, H: FsVerityHashValue>(fd: F) -> Result<()> { | ||
unsafe { | ||
ioctl::ioctl( | ||
fd, | ||
ioctl::Setter::<FsIocEnableVerity, FsVerityEnableArg>::new(FsVerityEnableArg { | ||
version: 1, | ||
hash_algorithm: H::ALGORITHM as u32, | ||
block_size: 4096, | ||
salt_size: 0, | ||
salt_ptr: 0, | ||
sig_size: 0, | ||
__reserved1: 0, | ||
sig_ptr: 0, | ||
__reserved2: [0; 11], | ||
}), | ||
)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[repr(C)] | ||
pub struct FsVerityDigest<F> { | ||
digest_algorithm: u16, | ||
digest_size: u16, | ||
digest: F, | ||
} | ||
|
||
// #define FS_IOC_MEASURE_VERITY _IORW('f', 134, struct fsverity_digest) | ||
type FsIocMeasureVerity = ioctl::ReadWriteOpcode<b'f', 134, FsVerityDigest<()>>; | ||
|
||
pub fn fs_ioc_measure_verity<F: AsFd, H: FsVerityHashValue>(fd: F) -> Result<H> { | ||
let digest_size = std::mem::size_of::<H>() as u16; | ||
let digest_algorithm = H::ALGORITHM as u16; | ||
|
||
let mut digest = FsVerityDigest::<H> { | ||
digest_algorithm, | ||
digest_size, | ||
digest: H::EMPTY, | ||
}; | ||
|
||
unsafe { | ||
ioctl::ioctl( | ||
fd, | ||
ioctl::Updater::<FsIocMeasureVerity, FsVerityDigest<H>>::new(&mut digest), | ||
)?; | ||
} | ||
|
||
if digest.digest_algorithm != digest_algorithm || digest.digest_size != digest_size { | ||
Err(std::io::Error::from(std::io::ErrorKind::InvalidData))? | ||
} else { | ||
Ok(digest.digest) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
pub mod digest; | ||
pub mod ioctl; | ||
|
||
pub trait FsVerityHashValue { | ||
const ALGORITHM: u8; | ||
const EMPTY: Self; | ||
} | ||
|
||
pub type Sha256HashValue = [u8; 32]; | ||
|
||
impl FsVerityHashValue for Sha256HashValue { | ||
const ALGORITHM: u8 = 1; | ||
const EMPTY: Self = [0; 32]; | ||
} | ||
|
||
pub type Sha512HashValue = [u8; 64]; | ||
|
||
impl FsVerityHashValue for Sha512HashValue { | ||
const ALGORITHM: u8 = 2; | ||
const EMPTY: Self = [0; 64]; | ||
} |