use crate::aes::{Aes, AesKey, Block, BlockCipher, BLOCK_SIZE};
use crate::aescrypt::{self, DecryptInfo, DecryptOptions, EncryptParams};
use crate::detect::Version;
use crate::zeroize::Zeroizing;
use crate::{Error, StreamError};
use core::fmt;
use core::marker::PhantomData;
use std::fs::File;
use std::io::{self, BufReader, BufWriter, Read, Write};
use std::ops::Range;
use std::path::Path;
pub use crate::aescrypt::{
DerivedKey, Extension, Header, Iterations, Limits, Password, PublicIv, SessionIv, SessionKey,
};
#[derive(Debug)]
#[non_exhaustive]
pub enum EncryptKey {
Password(Password),
Derived(DerivedKey),
}
impl EncryptKey {
pub fn password(text: &str) -> Result<Self, Error> {
Ok(EncryptKey::Password(Password::new(text)?))
}
pub fn raw_password(bytes: impl Into<Vec<u8>>) -> Self {
EncryptKey::Password(Password::from_raw_bytes(bytes))
}
pub fn derived(bytes: &[u8]) -> Result<Self, Error> {
Ok(EncryptKey::Derived(DerivedKey::try_from(bytes)?))
}
fn as_engine_key(&self) -> aescrypt::EncryptKey<'_> {
match self {
EncryptKey::Password(p) => aescrypt::EncryptKey::Password(p),
EncryptKey::Derived(k) => aescrypt::EncryptKey::Derived(k),
}
}
}
impl From<Password> for EncryptKey {
fn from(password: Password) -> Self {
EncryptKey::Password(password)
}
}
impl From<DerivedKey> for EncryptKey {
fn from(key: DerivedKey) -> Self {
EncryptKey::Derived(key)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum DecryptKey {
Password(Password),
Derived(DerivedKey),
Session(SessionIv, SessionKey),
}
impl DecryptKey {
pub fn password(text: &str) -> Result<Self, Error> {
Ok(DecryptKey::Password(Password::new(text)?))
}
pub fn raw_password(bytes: impl Into<Vec<u8>>) -> Self {
DecryptKey::Password(Password::from_raw_bytes(bytes))
}
pub fn derived(bytes: &[u8]) -> Result<Self, Error> {
Ok(DecryptKey::Derived(DerivedKey::try_from(bytes)?))
}
pub fn session(iv: &[u8], key: &[u8]) -> Result<Self, Error> {
Ok(DecryptKey::Session(SessionIv::try_from(iv)?, SessionKey::try_from(key)?))
}
fn as_engine_key(&self) -> aescrypt::DecryptKey<'_> {
match self {
DecryptKey::Password(p) => aescrypt::DecryptKey::Password(p),
DecryptKey::Derived(k) => aescrypt::DecryptKey::Derived(k),
DecryptKey::Session(iv, k) => aescrypt::DecryptKey::Session(iv, k),
}
}
}
impl From<Password> for DecryptKey {
fn from(password: Password) -> Self {
DecryptKey::Password(password)
}
}
impl From<DerivedKey> for DecryptKey {
fn from(key: DerivedKey) -> Self {
DecryptKey::Derived(key)
}
}
mod sealed {
use super::*;
pub trait Values {
fn values(&self) -> Result<(PublicIv, SessionIv, SessionKey), Error>;
}
pub trait Verify {
const VERIFY: bool;
}
}
pub trait ValueSource: sealed::Values {}
#[derive(Clone, Copy, Debug, Default)]
pub struct Random;
#[derive(Debug)]
pub struct Deterministic {
public_iv: PublicIv,
session_iv: SessionIv,
session_key: SessionKey,
}
impl sealed::Values for Random {
fn values(&self) -> Result<(PublicIv, SessionIv, SessionKey), Error> {
Ok((PublicIv::generate()?, SessionIv::generate()?, SessionKey::generate()?))
}
}
impl sealed::Values for Deterministic {
fn values(&self) -> Result<(PublicIv, SessionIv, SessionKey), Error> {
Ok((self.public_iv, self.session_iv, self.session_key.clone_secret()))
}
}
impl ValueSource for Random {}
impl ValueSource for Deterministic {}
#[derive(Debug)]
pub struct RawEncryptor<S: ValueSource = Random> {
key: EncryptKey,
version: Version,
iterations: Iterations,
extensions: Vec<Extension>,
source: S,
constant_time: bool,
}
impl RawEncryptor<Random> {
pub fn new(key: EncryptKey) -> Self {
RawEncryptor {
key,
version: Version::V3,
iterations: Iterations::DEFAULT,
extensions: Vec::new(),
source: Random,
constant_time: false,
}
}
pub fn deterministic(
self,
public_iv: PublicIv,
session_iv: SessionIv,
session_key: SessionKey,
) -> RawEncryptor<Deterministic> {
RawEncryptor {
key: self.key,
version: self.version,
iterations: self.iterations,
extensions: self.extensions,
source: Deterministic { public_iv, session_iv, session_key },
constant_time: self.constant_time,
}
}
}
impl<S: ValueSource> RawEncryptor<S> {
pub fn version(mut self, version: Version) -> Self {
self.version = version;
self
}
pub fn iterations(mut self, iterations: Iterations) -> Self {
self.iterations = iterations;
self
}
pub fn extension(mut self, extension: Extension) -> Self {
self.extensions.push(extension);
self
}
pub fn require_constant_time(mut self) -> Self {
self.constant_time = true;
self
}
fn stream<R: Read, W: Write>(&self, reader: R, writer: W) -> Result<u64, Error> {
if self.constant_time {
crate::aes::Backend::constant_time()?;
}
let (public_iv, session_iv, session_key) = sealed::Values::values(&self.source)?;
let params = EncryptParams {
version: self.version,
key: self.key.as_engine_key(),
iterations: self.iterations,
extensions: &self.extensions,
public_iv,
session_iv,
session_key: &session_key,
};
aescrypt::encrypt_engine(¶ms, reader, writer)
}
pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
let mut out = Vec::with_capacity(plaintext.len().saturating_add(256));
self.stream(plaintext, &mut out)?;
Ok(out)
}
pub fn encrypt_stream<R: Read, W: Write>(&self, reader: R, writer: W) -> Result<u64, Error> {
self.stream(reader, writer)
}
pub fn encrypt_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, input: P, output: Q) -> Result<u64, Error> {
let reader = BufReader::new(File::open(input)?);
aescrypt::write_atomically(output.as_ref(), |file| self.stream(reader, BufWriter::new(file)))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[must_use]
pub struct Verification {
pub key_block: Option<bool>,
pub message: bool,
pub final_block: Option<bool>,
}
impl Verification {
pub fn is_authentic(&self) -> bool {
self.message && self.key_block != Some(false)
}
pub fn is_consistent(&self) -> bool {
self.is_authentic() && self.final_block != Some(false)
}
}
#[must_use]
pub struct DecryptReport {
header: Header,
plaintext_len: u64,
derived_key: Option<DerivedKey>,
session_iv: SessionIv,
session_key: SessionKey,
verification: Verification,
}
impl From<DecryptInfo> for DecryptReport {
fn from(info: DecryptInfo) -> Self {
DecryptReport {
header: info.header,
plaintext_len: info.plaintext_len,
derived_key: info.derived_key,
session_iv: info.session_iv,
session_key: info.session_key,
verification: Verification {
key_block: info.key_hmac_ok,
message: info.message_hmac_ok,
final_block: info.final_block_ok,
},
}
}
}
impl fmt::Debug for DecryptReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DecryptReport")
.field("header", &self.header)
.field("plaintext_len", &self.plaintext_len)
.field("derived_key", &self.derived_key)
.field("session_iv", &self.session_iv)
.field("session_key", &self.session_key)
.field("verification", &self.verification)
.finish()
}
}
impl DecryptReport {
pub fn header(&self) -> &Header {
&self.header
}
pub fn plaintext_len(&self) -> u64 {
self.plaintext_len
}
pub fn derived_key(&self) -> Option<&DerivedKey> {
self.derived_key.as_ref()
}
pub fn session_iv(&self) -> &SessionIv {
&self.session_iv
}
pub fn session_key(&self) -> &SessionKey {
&self.session_key
}
pub fn verification(&self) -> Verification {
self.verification
}
}
#[derive(Debug)]
#[must_use]
pub struct Decrypted {
plaintext: Zeroizing<Vec<u8>>,
report: DecryptReport,
}
impl Decrypted {
pub fn plaintext(&self) -> &[u8] {
&self.plaintext
}
pub fn report(&self) -> &DecryptReport {
&self.report
}
pub fn into_parts(self) -> (Zeroizing<Vec<u8>>, DecryptReport) {
(self.plaintext, self.report)
}
}
#[must_use]
pub struct Unauthenticated<T> {
value: T,
verification: Verification,
}
impl<T> Unauthenticated<T> {
pub fn verification(&self) -> Verification {
self.verification
}
pub fn is_authentic(&self) -> bool {
self.verification.is_authentic()
}
pub fn into_authentic(self) -> Result<T, Error> {
if self.is_authentic() {
Ok(self.value)
} else {
Err(Error::AuthenticationFailed)
}
}
pub fn assume_authentic(self) -> T {
self.value
}
pub fn peek_unauthenticated(&self) -> &T {
&self.value
}
}
impl<T> fmt::Debug for Unauthenticated<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Unauthenticated").field("verification", &self.verification).finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug)]
pub struct Verified;
#[derive(Clone, Copy, Debug)]
pub struct Unverified;
impl sealed::Verify for Verified {
const VERIFY: bool = true;
}
impl sealed::Verify for Unverified {
const VERIFY: bool = false;
}
pub trait VerifyMode: sealed::Verify {}
impl VerifyMode for Verified {}
impl VerifyMode for Unverified {}
#[derive(Debug)]
pub struct RawDecryptor<V: VerifyMode = Verified> {
key: DecryptKey,
limits: Limits,
constant_time: bool,
mode: PhantomData<V>,
}
impl RawDecryptor<Verified> {
pub fn new(key: DecryptKey) -> Self {
RawDecryptor { key, limits: Limits::DEFAULT, constant_time: false, mode: PhantomData }
}
pub fn skip_verification(self) -> RawDecryptor<Unverified> {
RawDecryptor { key: self.key, limits: self.limits, constant_time: self.constant_time, mode: PhantomData }
}
pub fn decrypt(&self, data: &[u8]) -> Result<Decrypted, Error> {
self.decrypt_to_memory(data)
}
pub fn decrypt_stream<R: Read, W: Write>(&self, reader: R, writer: W) -> Result<DecryptReport, Error> {
self.run(reader, writer)
}
pub fn decrypt_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, input: P, output: Q) -> Result<DecryptReport, Error> {
self.run_file(input.as_ref(), output.as_ref())
}
}
impl RawDecryptor<Unverified> {
pub fn decrypt(&self, data: &[u8]) -> Result<Unauthenticated<Decrypted>, Error> {
let decrypted = self.decrypt_to_memory(data)?;
let verification = decrypted.report.verification;
Ok(Unauthenticated { value: decrypted, verification })
}
pub fn decrypt_stream<R: Read, W: Write>(&self, reader: R, writer: W) -> Result<Unauthenticated<DecryptReport>, Error> {
let report = self.run(reader, writer)?;
let verification = report.verification;
Ok(Unauthenticated { value: report, verification })
}
pub fn decrypt_file<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
input: P,
output: Q,
) -> Result<Unauthenticated<DecryptReport>, Error> {
let report = self.run_file(input.as_ref(), output.as_ref())?;
let verification = report.verification;
Ok(Unauthenticated { value: report, verification })
}
}
impl<V: VerifyMode> RawDecryptor<V> {
pub fn limits(mut self, limits: Limits) -> Self {
self.limits = limits;
self
}
pub fn require_constant_time(mut self) -> Self {
self.constant_time = true;
self
}
fn run<R: Read, W: Write>(&self, reader: R, writer: W) -> Result<DecryptReport, Error> {
if self.constant_time {
crate::aes::Backend::constant_time()?;
}
let options = DecryptOptions { limits: self.limits, verify: <V as sealed::Verify>::VERIFY };
Ok(aescrypt::decrypt_engine(self.key.as_engine_key(), &options, reader, writer)?.into())
}
fn decrypt_to_memory(&self, data: &[u8]) -> Result<Decrypted, Error> {
let mut plaintext = Zeroizing::new(Vec::with_capacity(data.len()));
let report = self.run(data, &mut *plaintext)?;
Ok(Decrypted { plaintext, report })
}
fn run_file(&self, input: &Path, output: &Path) -> Result<DecryptReport, Error> {
let reader = BufReader::new(File::open(input)?);
aescrypt::write_atomically(output, |file| self.run(reader, BufWriter::new(file)))
}
}
pub fn verify(key: &DecryptKey, data: &[u8]) -> Result<Verification, Error> {
let options = DecryptOptions { limits: Limits::DEFAULT, verify: false };
let info = aescrypt::decrypt_engine(key.as_engine_key(), &options, data, io::sink())?;
Ok(Verification { key_block: info.key_hmac_ok, message: info.message_hmac_ok, final_block: info.final_block_ok })
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Layout {
pub header: Header,
pub extensions: Vec<(Range<usize>, Extension)>,
pub key_block: Option<(Range<usize>, [u8; 48])>,
pub key_block_hmac: Option<(Range<usize>, [u8; 32])>,
pub ciphertext: Range<usize>,
pub final_block_size: Option<u8>,
pub message_hmac: (Range<usize>, [u8; 32]),
pub total_len: usize,
}
impl Layout {
pub fn ciphertext_len(&self) -> usize {
self.ciphertext.len()
}
pub fn plaintext_len(&self) -> Option<usize> {
let size = (self.final_block_size? & 0x0f) as usize;
let len = self.ciphertext.len();
Some(match len.checked_sub(BLOCK_SIZE) {
Some(full_blocks) if size != 0 => full_blocks.saturating_add(size),
_ => len,
})
}
}
fn field<const N: usize>(data: &[u8], range: &Range<usize>) -> Result<[u8; N], Error> {
data.get(range.clone())
.and_then(|bytes| bytes.try_into().ok())
.ok_or(Error::InvalidStream(StreamError::Truncated))
}
pub fn inspect(data: &[u8]) -> Result<Layout, Error> {
inspect_with_limits(data, Limits::DEFAULT)
}
pub fn inspect_with_limits(data: &[u8], limits: Limits) -> Result<Layout, Error> {
let header = aescrypt::read_header_with_limits(data, limits)?;
let version = header.version();
let mut extensions = Vec::new();
if version >= Version::V2 {
let mut pos: usize = 5;
for ext in header.extensions() {
let start = pos.saturating_add(2);
let end = start.saturating_add(ext.as_bytes().len());
extensions.push((start..end, ext.clone()));
pos = end;
}
}
let mut pos = header.len();
let mut take = |n: usize| -> Range<usize> {
let range = pos..pos.saturating_add(n);
pos = range.end;
range
};
let (key_block, key_block_hmac) = if version >= Version::V1 {
let block = take(48);
let hmac = take(32);
let block_bytes = field(data, &block)?;
let hmac_bytes = field(data, &hmac)?;
(Some((block, block_bytes)), Some((hmac, hmac_bytes)))
} else {
(None, None)
};
let trailer = match version {
Version::V1 | Version::V2 => 33,
_ => 32,
};
let ciphertext_end = data
.len()
.checked_sub(trailer)
.filter(|&end| end >= pos)
.ok_or(Error::InvalidStream(StreamError::Truncated))?;
let ciphertext = pos..ciphertext_end;
if ciphertext.len() % BLOCK_SIZE != 0 {
return Err(Error::InvalidStream(StreamError::UnalignedCiphertext));
}
let final_block_size = match version {
Version::V0 => Some(header.reserved()),
Version::V1 | Version::V2 => data.get(ciphertext.end).copied(),
_ => None,
};
let hmac = data.len().saturating_sub(32)..data.len();
let hmac_bytes = field(data, &hmac)?;
Ok(Layout {
header,
extensions,
key_block,
key_block_hmac,
ciphertext,
final_block_size,
message_hmac: (hmac, hmac_bytes),
total_len: data.len(),
})
}
pub fn ecb_encrypt(key: &AesKey, data: &[u8]) -> Result<Vec<u8>, Error> {
ecb(key, data, |cipher, block| cipher.encrypt_block(block))
}
pub fn ecb_decrypt(key: &AesKey, data: &[u8]) -> Result<Vec<u8>, Error> {
ecb(key, data, |cipher, block| cipher.decrypt_block(block))
}
fn ecb(key: &AesKey, data: &[u8], op: impl Fn(&Aes, &mut Block)) -> Result<Vec<u8>, Error> {
if data.len() % BLOCK_SIZE != 0 {
return Err(Error::InvalidCiphertextLength(data.len()));
}
let cipher = Aes::from_key(key);
let mut out = data.to_vec();
for chunk in out.chunks_exact_mut(BLOCK_SIZE) {
let mut block = [0u8; BLOCK_SIZE];
block.copy_from_slice(chunk);
op(&cipher, &mut block);
chunk.copy_from_slice(&block);
}
Ok(out)
}