|
@@ -2,7 +2,30 @@
|
|
|
|
|
|
mod tpm;
|
|
|
|
|
|
-use super::*;
|
|
|
+use super::{
|
|
|
+ Block,
|
|
|
+ Writecap,
|
|
|
+ Serialize,
|
|
|
+ Deserialize,
|
|
|
+ Display,
|
|
|
+ Formatter,
|
|
|
+ fmt,
|
|
|
+ Hashable,
|
|
|
+ BigArray,
|
|
|
+ Owned,
|
|
|
+ Principal,
|
|
|
+ Path,
|
|
|
+ HashMap,
|
|
|
+ Epoch,
|
|
|
+ io,
|
|
|
+ Read,
|
|
|
+ Write,
|
|
|
+ Seek,
|
|
|
+ Header,
|
|
|
+ Compose,
|
|
|
+ Sectored,
|
|
|
+ SECTOR_SZ_DEFAULT,
|
|
|
+};
|
|
|
|
|
|
use openssl::{
|
|
|
error::ErrorStack,
|
|
@@ -21,7 +44,7 @@ use serde::{
|
|
|
};
|
|
|
use zeroize::ZeroizeOnDrop;
|
|
|
use std::{
|
|
|
- str::FromStr, num::TryFromIntError, marker::PhantomData,
|
|
|
+ str::FromStr, num::TryFromIntError, marker::PhantomData, io::{ErrorKind, SeekFrom},
|
|
|
};
|
|
|
use strum_macros::{EnumString, EnumDiscriminants, Display};
|
|
|
use foreign_types::ForeignType;
|
|
@@ -45,15 +68,16 @@ pub enum Error {
|
|
|
BlockNotEncrypted,
|
|
|
InvalidHashFormat,
|
|
|
InvalidSignature,
|
|
|
- IncorrectSize(usize),
|
|
|
+ IncorrectSize { expected: usize, actual: usize },
|
|
|
+ IndivisibleSize { divisor: usize, actual: usize },
|
|
|
WritecapAuthzErr(WritecapAuthzErr),
|
|
|
Serde(serde_block_tree::Error),
|
|
|
Io(std::io::Error),
|
|
|
- Custom(Box<dyn std::fmt::Debug>)
|
|
|
+ Custom(Box<dyn std::fmt::Debug + Send + Sync>)
|
|
|
}
|
|
|
|
|
|
impl Error {
|
|
|
- fn custom<E: std::fmt::Debug + 'static>(err: E) -> Self {
|
|
|
+ fn custom<E: std::fmt::Debug + Send + Sync + 'static>(err: E) -> Self {
|
|
|
Error::Custom(Box::new(err))
|
|
|
}
|
|
|
}
|
|
@@ -68,7 +92,13 @@ impl Display for Error {
|
|
|
Error::BlockNotEncrypted => write!(f, "block was not encrypted"),
|
|
|
Error::InvalidHashFormat => write!(f, "invalid format"),
|
|
|
Error::InvalidSignature => write!(f, "invalid signature"),
|
|
|
- Error::IncorrectSize(size) => write!(f, "incorrect size: {}", size),
|
|
|
+ Error::IncorrectSize { expected, actual }
|
|
|
+ => write!(f, "expected size {} but got {}", expected, actual),
|
|
|
+ Error::IndivisibleSize { divisor, actual }
|
|
|
+ => write!(
|
|
|
+ f,
|
|
|
+ "expected a size which is divisible by {} but got {}", divisor, actual
|
|
|
+ ),
|
|
|
Error::WritecapAuthzErr(err) => err.fmt(f),
|
|
|
Error::Serde(err) => err.fmt(f),
|
|
|
Error::Io(err) => err.fmt(f),
|
|
@@ -77,6 +107,8 @@ impl Display for Error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+impl std::error::Error for Error {}
|
|
|
+
|
|
|
impl From<WritecapAuthzErr> for Error {
|
|
|
fn from(err: WritecapAuthzErr) -> Self {
|
|
|
Error::WritecapAuthzErr(err)
|
|
@@ -107,6 +139,12 @@ impl From<TryFromIntError> for Error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+impl From<Error> for io::Error {
|
|
|
+ fn from(err: Error) -> Self {
|
|
|
+ io::Error::new(io::ErrorKind::Other, err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
|
|
|
|
|
/// Returns an array of the given length filled with cryptographically strong random data.
|
|
@@ -293,7 +331,7 @@ impl AeadKeyKind {
|
|
|
fn array_from<const N: usize>(slice: &[u8]) -> Result<[u8; N]> {
|
|
|
let slice_len = slice.len();
|
|
|
if N != slice_len {
|
|
|
- return Err(Error::IncorrectSize(slice_len))
|
|
|
+ return Err(Error::IncorrectSize { actual: slice_len, expected: N })
|
|
|
}
|
|
|
let mut array = [0u8; N];
|
|
|
array.copy_from_slice(slice);
|
|
@@ -409,6 +447,29 @@ impl SymKey {
|
|
|
};
|
|
|
SymParams { cipher, key, iv }
|
|
|
}
|
|
|
+
|
|
|
+ fn block_size(&self) -> usize {
|
|
|
+ let SymParams { cipher, .. } = self.params();
|
|
|
+ cipher.block_size()
|
|
|
+ }
|
|
|
+
|
|
|
+ // The number of bytes that the plaintext expands by when encrypted.
|
|
|
+ fn expansion_sz(&self) -> usize {
|
|
|
+ match self {
|
|
|
+ SymKey::Aes256Cbc { .. } => 16,
|
|
|
+ SymKey::Aes256Ctr { .. } => 0,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn to_encrypter(&self) -> Result<Crypter> {
|
|
|
+ let SymParams { cipher, key, iv } = self.params();
|
|
|
+ Ok(Crypter::new(cipher, Mode::Encrypt, key, iv)?)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn to_decrypter(&self) -> Result<Crypter> {
|
|
|
+ let SymParams { cipher, key, iv } = self.params();
|
|
|
+ Ok(Crypter::new(cipher, Mode::Decrypt, key, iv)?)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl Encrypter for SymKey {
|
|
@@ -1038,6 +1099,193 @@ pub(crate) trait CredStore {
|
|
|
) -> Result<Self::CredHandle>;
|
|
|
}
|
|
|
|
|
|
+struct MerkleStream<T> {
|
|
|
+ inner: T,
|
|
|
+ pos: usize,
|
|
|
+ sector_len: usize,
|
|
|
+ //tree: ???,
|
|
|
+}
|
|
|
+
|
|
|
+impl MerkleStream<()> {
|
|
|
+ fn new() -> Self {
|
|
|
+ MerkleStream {
|
|
|
+ inner: (),
|
|
|
+ pos: 0,
|
|
|
+ sector_len: SECTOR_SZ_DEFAULT,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> Compose<T, MerkleStream<T>> for MerkleStream<()> {
|
|
|
+ fn compose(self, inner: T) -> MerkleStream<T> {
|
|
|
+ MerkleStream {
|
|
|
+ inner,
|
|
|
+ pos: 0,
|
|
|
+ sector_len: self.sector_len,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: Write> Write for MerkleStream<T> {
|
|
|
+ fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
|
|
|
+ unimplemented!()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn flush(&mut self) -> io::Result<()> {
|
|
|
+ unimplemented!()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: Read> Read for MerkleStream<T> {
|
|
|
+ fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
|
|
+ unimplemented!()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: Seek> Seek for MerkleStream<T> {
|
|
|
+ fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
|
|
|
+ unimplemented!()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// A stream which encrypts all data written to it and decrypts all data read from it.
|
|
|
+struct SecretStream<T> {
|
|
|
+ inner: T,
|
|
|
+ key: SymKey,
|
|
|
+ /// Buffer for ciphertext.
|
|
|
+ ct_buf: Vec<u8>,
|
|
|
+ /// Buffer for plaintext.
|
|
|
+ pt_buf: Vec<u8>,
|
|
|
+ // The sector size of this stream. Reads and writes are only accepted for buffers of this size.
|
|
|
+ sect_sz: usize,
|
|
|
+ // The sector size of the inner stream. Reads and writes are only executed using buffers of
|
|
|
+ // this size.
|
|
|
+ inner_sect_sz: usize,
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> SecretStream<T> {
|
|
|
+ fn new(key: SymKey, inner: T, inner_sect_sz: usize) -> Result<SecretStream<T>> {
|
|
|
+ let expansion_sz = key.expansion_sz();
|
|
|
+ let sect_sz = inner_sect_sz - expansion_sz;
|
|
|
+ let block_sz = key.block_size();
|
|
|
+ if 0 != sect_sz % block_sz {
|
|
|
+ return Err(Error::IndivisibleSize { divisor: block_sz, actual: sect_sz })
|
|
|
+ }
|
|
|
+ let ct_buf = vec![0u8; inner_sect_sz];
|
|
|
+ let pt_buf = vec![0u8; inner_sect_sz + block_sz];
|
|
|
+ Ok(SecretStream { inner, key, ct_buf, pt_buf, sect_sz, inner_sect_sz, })
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Given an offset into this stream, produces the corresponding offset into the inner stream.
|
|
|
+ fn inner_offset(&self, outer_offset: u64) -> u64{
|
|
|
+ let sect_sz = self.sect_sz as u64;
|
|
|
+ let inner_sect_sz = self.inner_sect_sz as u64;
|
|
|
+ // We return the offset into the current sector, plus the size of all previous sectors.
|
|
|
+ outer_offset % sect_sz + outer_offset / sect_sz * inner_sect_sz
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Given an offset into the inner stream, returns the corresponding offset into this stream.
|
|
|
+ fn outer_offset(&self, inner_offset: u64) -> u64{
|
|
|
+ let sect_sz = self.sect_sz as u64;
|
|
|
+ let inner_sect_sz = self.inner_sect_sz as u64;
|
|
|
+ inner_offset % inner_sect_sz + inner_offset / inner_sect_sz * sect_sz
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T, U: Sectored> Compose<U, SecretStream<U>> for SecretStream<T> {
|
|
|
+ fn compose(self, inner: U) -> SecretStream<U> {
|
|
|
+ let inner_sect_sz = inner.sector_sz();
|
|
|
+ SecretStream {
|
|
|
+ sect_sz: inner_sect_sz - self.key.expansion_sz(),
|
|
|
+ inner_sect_sz,
|
|
|
+ inner,
|
|
|
+ key: self.key,
|
|
|
+ ct_buf: self.ct_buf,
|
|
|
+ pt_buf: self.pt_buf,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> Sectored for SecretStream<T> {
|
|
|
+ fn sector_sz(&self) -> usize {
|
|
|
+ self.sect_sz
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: Write> Write for SecretStream<T> {
|
|
|
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
|
+ if buf.len() != self.sect_sz {
|
|
|
+ return Err(io::Error::new(
|
|
|
+ ErrorKind::Other,
|
|
|
+ Error::IncorrectSize { actual: buf.len(), expected: self.sect_sz, }
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ self.ct_buf.resize(self.inner_sect_sz, 0);
|
|
|
+ let mut encrypter = self.key.to_encrypter()?;
|
|
|
+ let mut count = encrypter.update(buf, &mut self.ct_buf)?;
|
|
|
+ count += encrypter.finalize(&mut self.ct_buf[count..])?;
|
|
|
+ self.ct_buf.truncate(count);
|
|
|
+
|
|
|
+ self.inner.write_all(&self.ct_buf).map(|_| buf.len())
|
|
|
+ }
|
|
|
+
|
|
|
+ fn flush(&mut self) -> io::Result<()> {
|
|
|
+ self.inner.flush()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: Read> Read for SecretStream<T> {
|
|
|
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
|
+ if buf.len() != self.sect_sz {
|
|
|
+ return Err(io::Error::new(
|
|
|
+ ErrorKind::Other,
|
|
|
+ Error::IncorrectSize { actual: buf.len(), expected: self.sect_sz, }
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ self.ct_buf.resize(self.inner_sect_sz, 0);
|
|
|
+ self.inner.read_exact(&mut self.ct_buf)?;
|
|
|
+
|
|
|
+ self.pt_buf.resize(self.inner_sect_sz + self.key.block_size(), 0);
|
|
|
+ let mut decrypter = self.key.to_decrypter()?;
|
|
|
+ let mut count = decrypter.update(&self.ct_buf, &mut self.pt_buf)?;
|
|
|
+ count += decrypter.finalize(&mut self.pt_buf[count..])?;
|
|
|
+ self.pt_buf.truncate(count);
|
|
|
+
|
|
|
+ buf.copy_from_slice(&self.pt_buf);
|
|
|
+ Ok(buf.len())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: Seek> Seek for SecretStream<T> {
|
|
|
+ fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
|
|
+ let outer_offset = match pos {
|
|
|
+ SeekFrom::Start(offset) => offset,
|
|
|
+ SeekFrom::Current(offset) => {
|
|
|
+ let inner_offset = self.inner.stream_position()?;
|
|
|
+ let outer_offset = self.outer_offset(inner_offset);
|
|
|
+ if offset >= 0 {
|
|
|
+ outer_offset + offset as u64
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ outer_offset - (-offset) as u64
|
|
|
+ }
|
|
|
+ }
|
|
|
+ SeekFrom::End(_) => {
|
|
|
+ // We can support this once stream_len is stabilized:
|
|
|
+ // https://github.com/rust-lang/rust/issues/59359
|
|
|
+ return Err(io::Error::new(
|
|
|
+ ErrorKind::Unsupported,
|
|
|
+ "seeking from the end of the stream is not supported"))
|
|
|
+ },
|
|
|
+ };
|
|
|
+ let inner_offset = self.inner_offset(outer_offset);
|
|
|
+ self.inner.seek(SeekFrom::Start(inner_offset))?;
|
|
|
+ Ok(outer_offset)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
pub(crate) fn encrypt_block<C: Encrypter + Decrypter>(
|
|
|
mut block: Block, principal: &Principal, creds: &C
|
|
|
) -> Result<Block> {
|
|
@@ -1221,10 +1469,17 @@ pub(crate) fn verify_writecap(mut writecap: &Writecap, path: &Path) -> Result<()
|
|
|
Err(WritecapAuthzErr::ChainTooLong(CHAIN_LEN_LIMIT).into())
|
|
|
}
|
|
|
|
|
|
+pub fn verify_header(_header: &Header, _sig: &Signature) -> Result<()> {
|
|
|
+ unimplemented!()
|
|
|
+}
|
|
|
+
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
use super::*;
|
|
|
- use test_helpers::*;
|
|
|
+ use super::super::test_helpers::*;
|
|
|
+ use std::{
|
|
|
+ time::Duration, io::{Cursor, SeekFrom},
|
|
|
+ };
|
|
|
|
|
|
fn encrypt_decrypt_block_test_case<C: Creds>(
|
|
|
mut actual: Block, principal: &Principal, creds: &C
|
|
@@ -1373,7 +1628,7 @@ mod tests {
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
- fn encrypt_decrypt_aes256gcm() {
|
|
|
+ fn aeadkey_encrypt_decrypt_aes256gcm() {
|
|
|
let key = AeadKey::new(AeadKeyKind::AesGcm256).expect("failed to create key");
|
|
|
let aad = [0u8; 16];
|
|
|
let expected = [0u8; 32];
|
|
@@ -1383,7 +1638,7 @@ mod tests {
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
- fn decrypt_fails_when_ct_modified() {
|
|
|
+ fn aeadkey_decrypt_fails_when_ct_modified() {
|
|
|
let key = AeadKey::new(AeadKeyKind::AesGcm256).expect("failed to create key");
|
|
|
let aad = [0u8; 16];
|
|
|
let expected = [0u8; 32];
|
|
@@ -1399,6 +1654,78 @@ mod tests {
|
|
|
assert!(result.is_err())
|
|
|
}
|
|
|
|
|
|
+ fn secret_stream(key_kind: SymKeyKind, num_sectors: usize) -> SecretStream<Cursor<Vec<u8>>> {
|
|
|
+ let key = SymKey::generate(key_kind).expect("key generation failed");
|
|
|
+ let inner = Cursor::new(vec![0u8; num_sectors * SECTOR_SZ_DEFAULT]);
|
|
|
+ SecretStream::new(key, inner, SECTOR_SZ_DEFAULT)
|
|
|
+ .expect("secret_stream creation failed")
|
|
|
+ }
|
|
|
+
|
|
|
+ fn secret_stream_encrypt_decrypt_are_inverse_test_case(key_kind: SymKeyKind) {
|
|
|
+ let mut stream = secret_stream(key_kind, 3);
|
|
|
+ let sector_sz = stream.sector_sz();
|
|
|
+ let expected = vec![1u8; 3 * sector_sz];
|
|
|
+
|
|
|
+ for sector in expected.chunks(sector_sz) {
|
|
|
+ stream.write(sector).expect("write failed");
|
|
|
+ }
|
|
|
+ stream.seek(SeekFrom::Start(0)).expect("seek failed");
|
|
|
+ let mut actual = vec![0u8; 3 * sector_sz];
|
|
|
+ for sector in actual.chunks_mut(sector_sz) {
|
|
|
+ stream.read(sector).expect("read failed");
|
|
|
+ }
|
|
|
+
|
|
|
+ assert_eq!(expected, actual);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn secret_stream_encrypt_decrypt_are_inverse_aes256cbc() {
|
|
|
+ secret_stream_encrypt_decrypt_are_inverse_test_case(SymKeyKind::Aes256Cbc)
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn secret_stream_encrypt_decrypt_are_inverse_aes256ctr() {
|
|
|
+ secret_stream_encrypt_decrypt_are_inverse_test_case(SymKeyKind::Aes256Ctr)
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn secret_stream_seek_from_start() {
|
|
|
+ let mut stream = secret_stream(SymKeyKind::Aes256Cbc, 3);
|
|
|
+ let sector_sz = stream.sector_sz();
|
|
|
+ let expected = vec![2u8; sector_sz];
|
|
|
+ // Write one sector of ones, one sector of twos and one sector of threes.
|
|
|
+ for k in 1..4 {
|
|
|
+ let sector: Vec<u8> = std::iter::repeat(k as u8).take(sector_sz).collect();
|
|
|
+ stream.write(§or).expect("writing to stream failed");
|
|
|
+ }
|
|
|
+
|
|
|
+ stream.seek(SeekFrom::Start(sector_sz as u64)).expect("seek failed");
|
|
|
+
|
|
|
+ // A read from the stream should now return the second sector, which is filled with twos.
|
|
|
+ let mut actual = vec![0u8; sector_sz];
|
|
|
+ stream.read(&mut actual).expect("reading from stream failed");
|
|
|
+ assert_eq!(expected, actual);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn secret_stream_seek_from_current() {
|
|
|
+ let mut stream = secret_stream(SymKeyKind::Aes256Cbc, 3);
|
|
|
+ let sector_sz = stream.sector_sz();
|
|
|
+ let expected = vec![3u8; sector_sz];
|
|
|
+ // Write one sector of ones, one sector of twos and one sector of threes.
|
|
|
+ for k in 1..4 {
|
|
|
+ let sector: Vec<u8> = std::iter::repeat(k as u8).take(sector_sz).collect();
|
|
|
+ stream.write(§or).expect("writing to stream failed");
|
|
|
+ }
|
|
|
+
|
|
|
+ stream.seek(SeekFrom::Current(-1 * (sector_sz as i64))).expect("seek failed");
|
|
|
+
|
|
|
+ // A read from the stream should now return the last sector, which is filled with threes.
|
|
|
+ let mut actual = vec![0u8; sector_sz];
|
|
|
+ stream.read(&mut actual).expect("reading from stream failed");
|
|
|
+ assert_eq!(expected, actual);
|
|
|
+ }
|
|
|
+
|
|
|
/// Tests that validate the dependencies of this module.
|
|
|
mod dependency_tests {
|
|
|
use super::*;
|