use zeroize::Zeroizing;
pub use crate::buffer::SegmentBuffer;
use crate::wire::split_header;
use crate::{
AEAD_MAX_SEGMENTS, DecryptionState, EncryptionState, Error, Header, Key, LengthRequirement,
Parameters, Result, SEGMENT_PREFIX_LENGTH, SegmentKind, length_usize_to_u64, start_decryption,
start_decryption_inferred, start_encryption,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct SegmentCounter {
next: u64,
closed: bool,
}
impl SegmentCounter {
const fn new() -> Self {
Self {
next: 0,
closed: false,
}
}
const fn next_position(self) -> u64 {
self.next
}
const fn is_finished(self) -> bool {
self.closed
}
fn position_for(self, kind: SegmentKind) -> Result<u64> {
if self.closed {
return Err(Error::Closed);
}
match kind {
SegmentKind::NonFinal if self.next == AEAD_MAX_SEGMENTS - 1 => Err(Error::SegmentLimit),
SegmentKind::Final if self.next >= AEAD_MAX_SEGMENTS => Err(Error::SegmentLimit),
SegmentKind::NonFinal | SegmentKind::Final => Ok(self.next),
}
}
fn complete(&mut self, kind: SegmentKind) {
match kind {
SegmentKind::NonFinal => self.next += 1,
SegmentKind::Final => self.closed = true,
}
}
const fn finish(self) -> Result<()> {
if self.closed {
Ok(())
} else {
Err(Error::Truncated)
}
}
}
#[derive(Debug)]
pub struct Encryptor {
state: EncryptionState,
header: Header,
counter: SegmentCounter,
}
pub struct FinalEncryptError {
encryptor: Box<Encryptor>,
error: Error,
}
impl FinalEncryptError {
#[must_use]
pub const fn error(&self) -> &Error {
&self.error
}
#[must_use]
pub fn into_encryptor(self) -> Encryptor {
*self.encryptor
}
#[must_use]
pub fn into_error(self) -> Error {
self.error
}
#[must_use]
pub fn into_parts(self) -> (Error, Encryptor) {
(self.error, *self.encryptor)
}
}
impl core::fmt::Debug for FinalEncryptError {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("FinalEncryptError")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
impl core::fmt::Display for FinalEncryptError {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
formatter,
"failed to encrypt final FLOE segment: {}",
self.error
)
}
}
impl std::error::Error for FinalEncryptError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
impl Encryptor {
pub fn new(key: &Key, aad: &[u8], parameters: Parameters) -> Result<Self> {
let (state, header) = start_encryption(key, aad, parameters)?;
Ok(Self {
state,
header,
counter: SegmentCounter::new(),
})
}
#[must_use]
pub const fn header(&self) -> &Header {
&self.header
}
#[must_use]
pub fn provider(&self) -> crate::Provider {
self.state.provider()
}
#[must_use]
pub fn parameters(&self) -> Parameters {
self.state.parameters()
}
#[must_use]
pub const fn next_position(&self) -> u64 {
self.counter.next_position()
}
fn advance<T>(&mut self, op: impl FnOnce(&mut EncryptionState, u64) -> Result<T>) -> Result<T> {
let position = self.counter.position_for(SegmentKind::NonFinal)?;
let value = op(&mut self.state, position)?;
self.counter.complete(SegmentKind::NonFinal);
Ok(value)
}
fn finalize<T>(
mut self,
op: impl FnOnce(&mut EncryptionState, u64) -> Result<T>,
) -> core::result::Result<T, FinalEncryptError> {
let result = self
.counter
.position_for(SegmentKind::Final)
.and_then(|position| op(&mut self.state, position));
result.map_err(|error| FinalEncryptError {
encryptor: Box::new(self),
error,
})
}
pub fn encrypt_non_final_segment(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
self.advance(|state, position| {
state.encrypt_segment_at(plaintext, position, SegmentKind::NonFinal)
})
}
pub fn encrypt_non_final_segment_into(
&mut self,
plaintext: &[u8],
output: &mut [u8],
) -> Result<usize> {
self.advance(|state, position| {
state.encrypt_segment_into_at(plaintext, position, SegmentKind::NonFinal, output)
})
}
pub fn encrypt_non_final_segment_in_place<'a>(
&mut self,
buffer: &'a mut SegmentBuffer,
) -> Result<&'a [u8]> {
self.advance(|state, position| {
state.encrypt_segment_in_place_at(buffer, position, SegmentKind::NonFinal)
})
}
pub fn encrypt_final_segment(
self,
plaintext: &[u8],
) -> core::result::Result<Vec<u8>, FinalEncryptError> {
self.finalize(|state, position| {
state.encrypt_segment_at(plaintext, position, SegmentKind::Final)
})
}
pub fn encrypt_final_segment_into(
self,
plaintext: &[u8],
output: &mut [u8],
) -> core::result::Result<usize, FinalEncryptError> {
self.finalize(|state, position| {
state.encrypt_segment_into_at(plaintext, position, SegmentKind::Final, output)
})
}
pub fn encrypt_final_segment_in_place(
self,
buffer: &mut SegmentBuffer,
) -> core::result::Result<&[u8], FinalEncryptError> {
self.finalize(|state, position| {
state.encrypt_segment_in_place_at(buffer, position, SegmentKind::Final)
})
}
}
#[derive(Debug)]
pub struct Decryptor {
state: DecryptionState,
counter: SegmentCounter,
}
impl Decryptor {
pub fn new(key: &Key, aad: &[u8], header: &Header) -> Result<Self> {
let state = start_decryption_inferred(key, aad, header)?;
Ok(Self {
state,
counter: SegmentCounter::new(),
})
}
pub fn new_with_parameters(
key: &Key,
aad: &[u8],
parameters: Parameters,
header: &Header,
) -> Result<Self> {
let state = start_decryption(key, aad, parameters, header)?;
Ok(Self {
state,
counter: SegmentCounter::new(),
})
}
#[must_use]
pub fn provider(&self) -> crate::Provider {
self.state.provider()
}
#[must_use]
pub fn parameters(&self) -> Parameters {
self.state.parameters()
}
#[must_use]
pub const fn next_position(&self) -> u64 {
self.counter.next_position()
}
#[must_use]
pub const fn is_finished(&self) -> bool {
self.counter.is_finished()
}
fn advance<T>(
&mut self,
kind: SegmentKind,
op: impl FnOnce(&mut DecryptionState, u64) -> Result<T>,
) -> Result<T> {
let position = self.counter.position_for(kind)?;
let value = op(&mut self.state, position)?;
self.counter.complete(kind);
Ok(value)
}
pub fn decrypt_segment(&mut self, ciphertext_segment: &[u8]) -> Result<Vec<u8>> {
let framing = self.framing(ciphertext_segment)?;
self.advance(framing.kind(), |state, position| {
state.decrypt_segment_at_framed(ciphertext_segment, position, framing)
})
}
pub fn decrypt_segment_into(
&mut self,
ciphertext_segment: &[u8],
output: &mut [u8],
) -> Result<usize> {
let framing = self.framing(ciphertext_segment)?;
self.decrypt_segment_into_framed(ciphertext_segment, framing, output)
}
pub(crate) fn decrypt_segment_into_framed(
&mut self,
ciphertext_segment: &[u8],
framing: crate::SegmentFraming,
output: &mut [u8],
) -> Result<usize> {
self.advance(framing.kind(), |state, position| {
state.decrypt_segment_into_at_framed(ciphertext_segment, position, framing, output)
})
}
pub fn decrypt_segment_in_place<'a>(
&mut self,
buffer: &'a mut SegmentBuffer,
) -> Result<&'a mut [u8]> {
let framing = self.framing(buffer.ciphertext()?)?;
self.advance(framing.kind(), |state, position| {
state.decrypt_segment_in_place_at_framed(buffer, position, framing)
})
}
pub fn finish(self) -> Result<()> {
self.counter.finish()
}
fn framing(&self, ciphertext_segment: &[u8]) -> Result<crate::SegmentFraming> {
let prefix = segment_prefix(ciphertext_segment, self.parameters())?;
crate::SegmentFraming::decode(self.parameters(), prefix)
}
}
fn segment_prefix(
ciphertext_segment: &[u8],
parameters: Parameters,
) -> Result<[u8; SEGMENT_PREFIX_LENGTH]> {
if ciphertext_segment.len() < SEGMENT_PREFIX_LENGTH {
return Err(Error::InvalidCiphertextLength {
actual: ciphertext_segment.len(),
required: LengthRequirement::Between {
minimum: SEGMENT_PREFIX_LENGTH,
maximum: parameters.ciphertext_segment_length(),
},
});
}
ciphertext_segment[..SEGMENT_PREFIX_LENGTH]
.try_into()
.map_err(|_| Error::InvalidSegmentPrefix)
}
pub fn encrypt(key: &Key, aad: &[u8], parameters: Parameters, plaintext: &[u8]) -> Result<Vec<u8>> {
let encryptor = Encryptor::new(key, aad, parameters)?;
encrypt_body(encryptor, plaintext)
}
fn encrypt_body(encryptor: Encryptor, plaintext: &[u8]) -> Result<Vec<u8>> {
let plaintext_length = length_usize_to_u64(plaintext.len());
let parameters = encryptor.parameters();
let layout = parameters.plaintext_layout(plaintext_length)?;
let capacity =
usize::try_from(layout.ciphertext_length()).map_err(|_| Error::LengthOverflow)?;
let mut ciphertext = Vec::with_capacity(capacity);
ciphertext.extend_from_slice(encryptor.header().as_ref());
let mut encryptor = Some(encryptor);
for segment in layout.segments() {
let plaintext_start =
usize::try_from(segment.plaintext_offset()).map_err(|_| Error::LengthOverflow)?;
let plaintext_end = plaintext_start
.checked_add(segment.plaintext_length())
.ok_or(Error::LengthOverflow)?;
let output_start = ciphertext.len();
let output_end = output_start
.checked_add(segment.ciphertext_length())
.ok_or(Error::LengthOverflow)?;
ciphertext.resize(output_end, 0);
let chunk = &plaintext[plaintext_start..plaintext_end];
let output = &mut ciphertext[output_start..];
match segment.kind() {
SegmentKind::NonFinal => {
encryptor
.as_mut()
.expect("only the last segment of a layout is final")
.encrypt_non_final_segment_into(chunk, output)?;
}
SegmentKind::Final => {
encryptor
.take()
.expect("every layout contains exactly one final segment")
.encrypt_final_segment_into(chunk, output)
.map_err(FinalEncryptError::into_error)?;
}
}
}
Ok(ciphertext)
}
pub fn decrypt(key: &Key, aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> {
let (header, body) = split_header(ciphertext)?;
let decryptor = Decryptor::new(key, aad, &header)?;
decrypt_body(decryptor, body)
}
pub fn decrypt_with_parameters(
key: &Key,
aad: &[u8],
parameters: Parameters,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
let (header, body) = split_header(ciphertext)?;
let decryptor = Decryptor::new_with_parameters(key, aad, parameters, &header)?;
decrypt_body(decryptor, body)
}
fn decrypt_body(mut decryptor: Decryptor, mut body: &[u8]) -> Result<Vec<u8>> {
let parameters = decryptor.parameters();
let mut plaintext = Zeroizing::new(Vec::with_capacity(body.len()));
while !body.is_empty() {
let prefix = segment_prefix(body, parameters)?;
let framing = crate::SegmentFraming::decode(parameters, prefix)?;
let segment_length = framing.ciphertext_length();
if body.len() < segment_length {
return Err(Error::InvalidCiphertextLength {
actual: body.len(),
required: LengthRequirement::AtLeast(segment_length),
});
}
if framing.is_final() && segment_length != body.len() {
return Err(Error::InvalidCiphertextLength {
actual: body.len(),
required: LengthRequirement::Exactly(segment_length),
});
}
let (segment, rest) = body.split_at(segment_length);
let start = plaintext.len();
plaintext.resize(start + framing.plaintext_length(), 0);
decryptor.decrypt_segment_into_framed(segment, framing, &mut plaintext[start..])?;
body = rest;
}
decryptor.finish()?;
Ok(core::mem::take(&mut *plaintext))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::key::test_key;
use crate::{HEADER_LENGTH, SEGMENT_OVERHEAD};
#[test]
fn online_states_reserve_last_position_for_final_segment() {
let key = test_key();
let parameters = Parameters::SEGMENT_4_KIB;
let mut encryption = Encryptor::new(&key, b"segment limit", parameters).unwrap();
let header = *encryption.header();
encryption.counter.next = AEAD_MAX_SEGMENTS - 1;
assert_eq!(
encryption.encrypt_non_final_segment(&[]),
Err(Error::SegmentLimit)
);
let final_segment = encryption.encrypt_final_segment(b"last").unwrap();
let mut decryption = Decryptor::new(&key, b"segment limit", &header).unwrap();
assert_eq!(decryption.parameters(), parameters);
decryption.counter.next = AEAD_MAX_SEGMENTS - 1;
assert_eq!(decryption.decrypt_segment(&final_segment).unwrap(), b"last");
assert!(decryption.is_finished());
decryption.finish().unwrap();
}
#[test]
fn complete_message_round_trips_boundaries() {
let parameters = Parameters::SEGMENT_4_KIB;
let segment_length = parameters.plaintext_segment_length();
for length in [
0,
1,
segment_length - 1,
segment_length,
segment_length + 1,
2 * segment_length,
2 * segment_length + 3,
] {
let plaintext: Vec<u8> = (0..length)
.map(|index| u8::try_from(index % 251).unwrap())
.collect();
let ciphertext = encrypt(&test_key(), b"aad", parameters, &plaintext).unwrap();
assert_eq!(
decrypt(&test_key(), b"aad", &ciphertext).unwrap(),
plaintext,
"round trip failed at length {length}"
);
}
}
#[test]
fn complete_message_decrypts_full_non_final_and_empty_final_segments() {
let parameters = Parameters::SEGMENT_4_KIB;
let plaintext = vec![0x5a; parameters.plaintext_segment_length()];
let mut encryption = Encryptor::new(&test_key(), b"alternate framing", parameters).unwrap();
let header = *encryption.header();
let non_final = encryption.encrypt_non_final_segment(&plaintext).unwrap();
let final_segment = encryption.encrypt_final_segment(b"").unwrap();
let mut ciphertext =
Vec::with_capacity(Header::LEN + non_final.len() + final_segment.len());
ciphertext.extend_from_slice(header.as_ref());
ciphertext.extend_from_slice(&non_final);
ciphertext.extend_from_slice(&final_segment);
let layout = parameters
.ciphertext_layout(u64::try_from(ciphertext.len()).unwrap())
.unwrap();
assert_eq!(layout.segment_count(), 2);
assert_eq!(
decrypt(&test_key(), b"alternate framing", &ciphertext).unwrap(),
plaintext
);
}
#[test]
fn finish_before_final_segment_reports_truncation() {
let parameters = Parameters::SEGMENT_4_KIB;
let encryption = Encryptor::new(&test_key(), b"aad", parameters).unwrap();
let header = *encryption.header();
let decryption = Decryptor::new(&test_key(), b"aad", &header).unwrap();
assert_eq!(decryption.finish(), Err(Error::Truncated));
}
#[test]
fn decryptor_closes_after_final_segment() {
let parameters = Parameters::SEGMENT_4_KIB;
let encryption = Encryptor::new(&test_key(), b"aad", parameters).unwrap();
assert_eq!(encryption.next_position(), 0);
let header = *encryption.header();
let final_segment = encryption.encrypt_final_segment(b"done").unwrap();
let mut decryption = Decryptor::new(&test_key(), b"aad", &header).unwrap();
assert_eq!(decryption.decrypt_segment(&final_segment).unwrap(), b"done");
assert!(decryption.is_finished());
assert_eq!(
decryption.decrypt_segment(&final_segment),
Err(Error::Closed)
);
assert!(decryption.finish().is_ok());
}
#[test]
fn decrypt_segment_into_requires_sufficient_output() {
let parameters = Parameters::SEGMENT_4_KIB;
let encryption = Encryptor::new(&test_key(), b"aad", parameters).unwrap();
let header = *encryption.header();
let final_segment = encryption.encrypt_final_segment(b"done").unwrap();
let mut decryption = Decryptor::new(&test_key(), b"aad", &header).unwrap();
assert_eq!(decryption.next_position(), 0);
let mut too_small = [0u8; 3];
assert!(matches!(
decryption.decrypt_segment_into(&final_segment, &mut too_small),
Err(Error::OutputTooSmall { .. })
));
let mut output = [0u8; 4];
assert_eq!(
decryption
.decrypt_segment_into(&final_segment, &mut output)
.unwrap(),
output.len()
);
assert_eq!(&output, b"done");
assert!(decryption.finish().is_ok());
}
#[test]
fn segments_shorter_than_minimum_overhead_rejected() {
let parameters = Parameters::SEGMENT_4_KIB;
let encryption = Encryptor::new(&test_key(), b"aad", parameters).unwrap();
let header = *encryption.header();
let mut invalid_prefix = [0u8; SEGMENT_OVERHEAD];
invalid_prefix[..SEGMENT_PREFIX_LENGTH]
.copy_from_slice(&u32::try_from(SEGMENT_OVERHEAD - 1).unwrap().to_be_bytes());
let mut decryption = Decryptor::new(&test_key(), b"aad", &header).unwrap();
assert!(matches!(
decryption.decrypt_segment(&invalid_prefix),
Err(Error::InvalidCiphertextLength {
actual,
required: LengthRequirement::Between {..},
}) if actual == SEGMENT_OVERHEAD - 1
));
}
#[test]
fn online_decrypt_paths_classify_prefix_length_mismatch_identically() {
let parameters = Parameters::SEGMENT_4_KIB;
let key = test_key();
let encryption = Encryptor::new(&key, b"framing consistency", parameters).unwrap();
let header = *encryption.header();
let segment = encryption.encrypt_final_segment(b"abcd").unwrap();
let declared = segment.len();
let truncated = &segment[..declared - 1];
let expected = || Error::InvalidCiphertextLength {
actual: declared - 1,
required: LengthRequirement::Exactly(declared),
};
let mut vec_path = Decryptor::new(&key, b"framing consistency", &header).unwrap();
assert_eq!(vec_path.decrypt_segment(truncated), Err(expected()));
let mut into_path = Decryptor::new(&key, b"framing consistency", &header).unwrap();
let mut output = [0u8; 8];
assert_eq!(
into_path.decrypt_segment_into(truncated, &mut output),
Err(expected())
);
let mut in_place_path = Decryptor::new(&key, b"framing consistency", &header).unwrap();
let mut buffer = SegmentBuffer::new(parameters);
buffer
.prepare_ciphertext(truncated.len())
.unwrap()
.copy_from_slice(truncated);
assert_eq!(
in_place_path.decrypt_segment_in_place(&mut buffer),
Err(expected())
);
for decryptor in [&vec_path, &into_path, &in_place_path] {
assert_eq!(decryptor.next_position(), 0);
assert!(!decryptor.is_finished());
}
}
#[test]
fn final_segment_round_trips_in_place() {
let parameters = Parameters::SEGMENT_4_KIB;
let encryption = Encryptor::new(&test_key(), b"aad", parameters).unwrap();
let header = *encryption.header();
let mut in_place = SegmentBuffer::new(parameters);
in_place
.prepare_plaintext(4)
.unwrap()
.copy_from_slice(b"done");
encryption
.encrypt_final_segment_in_place(&mut in_place)
.unwrap();
let mut decryption = Decryptor::new(&test_key(), b"aad", &header).unwrap();
assert_eq!(
decryption.decrypt_segment_in_place(&mut in_place).unwrap(),
b"done"
);
assert!(decryption.finish().is_ok());
}
fn failed_final_encryption() -> (Header, FinalEncryptError) {
let encryption =
Encryptor::new(&test_key(), b"recover final", Parameters::SEGMENT_4_KIB).unwrap();
let header = *encryption.header();
let failure = encryption
.encrypt_final_segment_into(b"retry", &mut [0u8; SEGMENT_OVERHEAD])
.unwrap_err();
(header, failure)
}
#[test]
fn failed_final_encryption_returns_reusable_state() {
let (header, failure) = failed_final_encryption();
assert!(matches!(failure.error(), Error::OutputTooSmall { .. }));
assert!(!format!("{failure:?}").contains("Encryptor"));
let encryption = failure.into_encryptor();
assert_eq!(encryption.header(), &header);
assert_eq!(encryption.next_position(), 0);
let encrypted = encryption.encrypt_final_segment(b"retry").unwrap();
let mut decryption = Decryptor::new(&test_key(), b"recover final", &header).unwrap();
assert_eq!(decryption.decrypt_segment(&encrypted).unwrap(), b"retry");
decryption.finish().unwrap();
}
#[test]
fn wrong_aad_fails_header_authentication() {
let parameters = Parameters::SEGMENT_4_KIB;
let ciphertext = encrypt(&test_key(), b"correct aad", parameters, b"plaintext").unwrap();
assert_eq!(
decrypt(&test_key(), b"wrong aad", &ciphertext),
Err(Error::InvalidHeaderTag)
);
}
#[test]
fn header_slices_require_exact_length() {
let parameters = Parameters::SEGMENT_4_KIB;
let ciphertext = encrypt(&test_key(), b"correct aad", parameters, b"plaintext").unwrap();
assert!(matches!(
Header::try_from(&ciphertext[..HEADER_LENGTH - 1]),
Err(Error::InvalidHeaderLength { .. })
));
assert!(matches!(
Header::try_from(&ciphertext[..=HEADER_LENGTH]),
Err(Error::InvalidHeaderLength { .. })
));
}
#[test]
fn flipped_header_bit_fails_header_authentication() {
let parameters = Parameters::SEGMENT_4_KIB;
let ciphertext = encrypt(&test_key(), b"correct aad", parameters, b"plaintext").unwrap();
let mut bad_header = ciphertext.clone();
bad_header[HEADER_LENGTH - 1] ^= 1;
assert_eq!(
decrypt(&test_key(), b"correct aad", &bad_header),
Err(Error::InvalidHeaderTag)
);
}
#[test]
fn flipped_ciphertext_bit_fails_segment_authentication() {
let parameters = Parameters::SEGMENT_4_KIB;
let ciphertext = encrypt(&test_key(), b"correct aad", parameters, b"plaintext").unwrap();
let mut bad_segment = ciphertext.clone();
*bad_segment.last_mut().unwrap() ^= 1;
assert_eq!(
decrypt(&test_key(), b"correct aad", &bad_segment),
Err(Error::AuthenticationFailed)
);
}
#[test]
fn truncated_ciphertexts_classified_by_missing_bytes() {
let parameters = Parameters::SEGMENT_4_KIB;
let ciphertext = encrypt(&test_key(), b"correct aad", parameters, b"plaintext").unwrap();
let header_only = &ciphertext[..HEADER_LENGTH];
assert_eq!(
decrypt(&test_key(), b"correct aad", header_only),
Err(Error::Truncated)
);
let truncated = &ciphertext[..ciphertext.len() - 1];
assert_eq!(
decrypt(&test_key(), b"correct aad", truncated),
Err(Error::InvalidCiphertextLength {
actual: truncated.len() - HEADER_LENGTH,
required: LengthRequirement::AtLeast(ciphertext.len() - HEADER_LENGTH)
})
);
let mut undersized_final = header_only.to_vec();
undersized_final.extend_from_slice(&4_u32.to_be_bytes());
assert!(matches!(
decrypt(&test_key(), b"correct aad", &undersized_final),
Err(Error::InvalidCiphertextLength { .. })
));
}
#[test]
fn every_truncation_of_valid_ciphertext_rejected() {
let parameters = Parameters::SEGMENT_4_KIB;
let plaintext = vec![0x3c; 3 * parameters.plaintext_segment_length() + 7];
let ciphertext = encrypt(&test_key(), b"aad", parameters, &plaintext).unwrap();
for end in 0..ciphertext.len() {
assert!(
decrypt(&test_key(), b"aad", &ciphertext[..end]).is_err(),
"accepted ciphertext truncated to {end} bytes"
);
}
}
#[test]
fn decrypt_with_parameters_rejects_mismatched_profile() {
let parameters = Parameters::SEGMENT_4_KIB;
let ciphertext = encrypt(&test_key(), b"aad", parameters, b"plaintext").unwrap();
assert_eq!(
decrypt_with_parameters(&test_key(), b"aad", Parameters::SEGMENT_1_MIB, &ciphertext,),
Err(Error::InvalidHeaderParameters)
);
}
#[test]
fn one_shot_decrypt_rejects_bytes_after_final_segment() {
let parameters = Parameters::SEGMENT_4_KIB;
let ciphertext = encrypt(&test_key(), b"aad", parameters, b"one shot").unwrap();
let final_segment_length = ciphertext.len() - HEADER_LENGTH;
let mut one_extra_byte = ciphertext.clone();
one_extra_byte.push(0);
let mut second_message = ciphertext.clone();
second_message.extend_from_slice(&ciphertext);
for trailing in [one_extra_byte, second_message] {
let actual_body_length = trailing.len() - HEADER_LENGTH;
assert_eq!(
decrypt(&test_key(), b"aad", &trailing),
Err(Error::InvalidCiphertextLength {
actual: actual_body_length,
required: LengthRequirement::Exactly(final_segment_length),
})
);
}
}
#[test]
fn final_encrypt_error_exposes_error_and_parts() {
let (header, failure) = failed_final_encryption();
assert!(
failure
.to_string()
.contains("failed to encrypt final FLOE segment")
);
assert!(matches!(
std::error::Error::source(&failure).and_then(|source| source.downcast_ref::<Error>()),
Some(Error::OutputTooSmall { .. })
));
let (error, encryption) = failure.into_parts();
assert!(matches!(error, Error::OutputTooSmall { .. }));
assert_eq!(encryption.header(), &header);
assert_eq!(encryption.next_position(), 0);
let (_, failure) = failed_final_encryption();
assert!(matches!(failure.into_error(), Error::OutputTooSmall { .. }));
}
#[test]
fn failed_in_place_encryption_empties_the_buffer() {
let parameters = Parameters::SEGMENT_4_KIB;
let mut encryption = Encryptor::new(&test_key(), b"in place", parameters).unwrap();
let mut buffer = SegmentBuffer::new(parameters);
buffer.prepare_plaintext(3).unwrap().copy_from_slice(b"abc");
let error = encryption
.encrypt_non_final_segment_in_place(&mut buffer)
.unwrap_err();
assert!(matches!(
error,
Error::InvalidPlaintextLength {
actual: 3,
required: LengthRequirement::Exactly(_),
}
));
assert_eq!(buffer.plaintext(), Err(Error::InvalidBufferState));
assert_eq!(buffer.ciphertext(), Err(Error::InvalidBufferState));
}
}