Skip to main content

fast_floe/
state.rs

1use std::sync::Arc;
2
3use subtle::ConstantTimeEq;
4use zeroize::{Zeroize, Zeroizing};
5
6use crate::backends::{AeadKey, ProviderRng};
7use crate::{
8    AEAD_IV_LENGTH, AEAD_MAX_SEGMENTS, AEAD_TAG_LENGTH, ENCODED_PARAMETERS_LENGTH, Error,
9    FLOE_IV_LENGTH, HEADER_LENGTH, HEADER_TAG_LENGTH, Key, LengthRequirement, Parameters, Provider,
10    Result, SEGMENT_OVERHEAD, SEGMENT_PAYLOAD_OFFSET, SEGMENT_PREFIX_LENGTH, SegmentBuffer,
11    SegmentFraming, SegmentKind, SegmentLayout, length_u32_to_usize, length_usize_to_u64,
12};
13
14const HEADER_TAG_PURPOSE: &[u8] = b"HEADER_TAG:";
15const MESSAGE_KEY_PURPOSE: &[u8] = b"MESSAGE_KEY:";
16const SEGMENT_KEY_PURPOSE: &[u8] = b"DEK:";
17const NONCE_BATCH_SIZE: usize = 256;
18const NONCE_BATCH_LENGTH: usize = AEAD_IV_LENGTH * NONCE_BATCH_SIZE;
19const SEGMENT_AAD_LENGTH: usize = 9;
20
21#[inline]
22fn segment_aad(position: u64, kind: SegmentKind) -> [u8; SEGMENT_AAD_LENGTH] {
23    let mut aad = [0u8; SEGMENT_AAD_LENGTH];
24    aad[..8].copy_from_slice(&position.to_be_bytes());
25    aad[8] = kind.indicator();
26    aad
27}
28
29/// A complete fixed-size FLOE header.
30///
31/// The encoded parameters are not authenticated until decryption
32/// initialization succeeds.
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub struct Header([u8; HEADER_LENGTH]);
35
36impl Header {
37    /// Length of every FLOE header in bytes.
38    pub const LEN: usize = HEADER_LENGTH;
39
40    const FLOE_IV_OFFSET: usize = ENCODED_PARAMETERS_LENGTH;
41    const TAG_OFFSET: usize = Self::FLOE_IV_OFFSET + FLOE_IV_LENGTH;
42
43    /// Returns the complete header bytes.
44    #[must_use]
45    pub const fn as_bytes(&self) -> &[u8; HEADER_LENGTH] {
46        &self.0
47    }
48
49    /// Decodes the parameter set declared by this header.
50    ///
51    /// This does not authenticate the header. Do not trust the result until
52    /// [`start_decryption`] or [`start_decryption_inferred`] successfully
53    /// authenticates the header.
54    ///
55    /// # Errors
56    ///
57    /// Returns [`Error::InvalidHeaderParameters`] if the encoded parameter set
58    /// is not supported.
59    pub fn unverified_parameters(&self) -> Result<Parameters> {
60        Parameters::decode(*self.encoded_parameters())
61    }
62
63    fn from_fields(
64        encoded: &[u8; ENCODED_PARAMETERS_LENGTH],
65        floe_iv: &[u8; FLOE_IV_LENGTH],
66        tag: &[u8; HEADER_TAG_LENGTH],
67    ) -> Self {
68        let mut bytes = [0u8; HEADER_LENGTH];
69        bytes[..Self::FLOE_IV_OFFSET].copy_from_slice(encoded);
70        bytes[Self::FLOE_IV_OFFSET..Self::TAG_OFFSET].copy_from_slice(floe_iv);
71        bytes[Self::TAG_OFFSET..].copy_from_slice(tag);
72        Self(bytes)
73    }
74
75    fn encoded_parameters(&self) -> &[u8; ENCODED_PARAMETERS_LENGTH] {
76        self.0[..Self::FLOE_IV_OFFSET]
77            .try_into()
78            .expect("header field offsets are compile-time constants")
79    }
80
81    fn floe_iv(&self) -> &[u8; FLOE_IV_LENGTH] {
82        self.0[Self::FLOE_IV_OFFSET..Self::TAG_OFFSET]
83            .try_into()
84            .expect("header field offsets are compile-time constants")
85    }
86
87    fn tag(&self) -> &[u8; HEADER_TAG_LENGTH] {
88        self.0[Self::TAG_OFFSET..]
89            .try_into()
90            .expect("header field offsets are compile-time constants")
91    }
92}
93
94impl AsRef<[u8]> for Header {
95    fn as_ref(&self) -> &[u8] {
96        &self.0
97    }
98}
99
100impl From<[u8; HEADER_LENGTH]> for Header {
101    fn from(bytes: [u8; HEADER_LENGTH]) -> Self {
102        Self(bytes)
103    }
104}
105
106impl From<Header> for [u8; HEADER_LENGTH] {
107    fn from(header: Header) -> Self {
108        header.0
109    }
110}
111
112impl TryFrom<&[u8]> for Header {
113    type Error = Error;
114
115    fn try_from(bytes: &[u8]) -> Result<Self> {
116        let bytes: [u8; HEADER_LENGTH] =
117            bytes.try_into().map_err(|_| Error::InvalidHeaderLength {
118                actual: bytes.len(),
119            })?;
120        Ok(Self(bytes))
121    }
122}
123
124struct SecretBytes<const N: usize>([u8; N]);
125
126impl<const N: usize> SecretBytes<N> {
127    fn expose_bytes(&self) -> &[u8] {
128        &self.0
129    }
130}
131
132impl<const N: usize> Drop for SecretBytes<N> {
133    fn drop(&mut self) {
134        self.0.zeroize();
135    }
136}
137
138struct CachedKey {
139    masked_position: u64,
140    key: AeadKey,
141}
142
143impl CachedKey {
144    fn derive(context: &MessageContext, masked_position: u64) -> Result<Self> {
145        Ok(Self {
146            masked_position,
147            key: derive_segment_key(
148                context.provider,
149                context.parameters,
150                &context.message_key,
151                &context.floe_iv,
152                &context.aad,
153                masked_position,
154            )?,
155        })
156    }
157}
158
159struct MessageContext {
160    provider: Provider,
161    parameters: Parameters,
162    message_key: SecretBytes<48>,
163    floe_iv: [u8; FLOE_IV_LENGTH],
164    aad: Box<[u8]>,
165}
166
167impl core::fmt::Debug for MessageContext {
168    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
169        f.debug_struct("MessageContext")
170            .field("provider", &self.provider)
171            .field("parameters", &self.parameters)
172            .field("floe_iv", &self.floe_iv)
173            .field("aad_length", &self.aad.len())
174            .finish_non_exhaustive()
175    }
176}
177
178impl MessageContext {
179    #[inline]
180    fn new(
181        provider: Provider,
182        parameters: Parameters,
183        message_key: SecretBytes<48>,
184        floe_iv: [u8; FLOE_IV_LENGTH],
185        aad: &[u8],
186    ) -> Self {
187        Self {
188            provider,
189            parameters,
190            message_key,
191            floe_iv,
192            aad: aad.into(),
193        }
194    }
195}
196
197struct KeyCache {
198    cached_key: Option<CachedKey>,
199}
200
201impl core::fmt::Debug for KeyCache {
202    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203        f.debug_struct("KeyCache")
204            .field(
205                "masked_position",
206                &self
207                    .cached_key
208                    .as_ref()
209                    .map(|cached| cached.masked_position),
210            )
211            .finish_non_exhaustive()
212    }
213}
214
215impl KeyCache {
216    #[inline]
217    fn new(context: &MessageContext) -> Result<Self> {
218        Ok(Self {
219            cached_key: Some(CachedKey::derive(context, 0)?),
220        })
221    }
222
223    #[inline]
224    const fn empty() -> Self {
225        Self { cached_key: None }
226    }
227
228    #[inline]
229    fn key_for_position(&mut self, context: &MessageContext, position: u64) -> Result<&AeadKey> {
230        if position >= AEAD_MAX_SEGMENTS {
231            return Err(Error::SegmentLimit);
232        }
233
234        let masked_position = context.parameters.masked_position(position);
235        if self
236            .cached_key
237            .as_ref()
238            .is_none_or(|cached| cached.masked_position != masked_position)
239        {
240            self.cached_key = Some(CachedKey::derive(context, masked_position)?);
241        }
242        let cached = self
243            .cached_key
244            .as_ref()
245            .expect("populated by the branch above");
246        Ok(&cached.key)
247    }
248}
249
250struct NonceGenerator {
251    rng: ProviderRng,
252    bytes: [u8; NONCE_BATCH_LENGTH],
253    next: usize,
254}
255
256impl core::fmt::Debug for NonceGenerator {
257    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
258        let remaining = (NONCE_BATCH_LENGTH - self.next) / AEAD_IV_LENGTH;
259        f.debug_struct("NonceGenerator")
260            .field("remaining", &remaining)
261            .finish_non_exhaustive()
262    }
263}
264
265impl NonceGenerator {
266    #[inline]
267    fn new(rng: ProviderRng) -> Self {
268        Self {
269            rng,
270            bytes: [0; NONCE_BATCH_LENGTH],
271            next: NONCE_BATCH_LENGTH,
272        }
273    }
274
275    #[inline]
276    fn next(&mut self) -> Result<[u8; AEAD_IV_LENGTH]> {
277        if self.next == NONCE_BATCH_LENGTH {
278            self.rng.fill(&mut self.bytes)?;
279            self.next = 0;
280        }
281        let mut nonce = [0; AEAD_IV_LENGTH];
282        nonce.copy_from_slice(&self.bytes[self.next..self.next + AEAD_IV_LENGTH]);
283        self.next += AEAD_IV_LENGTH;
284        Ok(nonce)
285    }
286}
287
288#[inline]
289fn ciphertext_segment_size(
290    context: &MessageContext,
291    plaintext_length: usize,
292    kind: SegmentKind,
293) -> Result<usize> {
294    let maximum = context.parameters.plaintext_segment_length();
295    match kind {
296        SegmentKind::Final if plaintext_length <= maximum => SEGMENT_OVERHEAD
297            .checked_add(plaintext_length)
298            .ok_or(Error::LengthOverflow),
299        SegmentKind::Final => Err(Error::InvalidPlaintextLength {
300            actual: plaintext_length,
301            required: LengthRequirement::AtMost(maximum),
302        }),
303        SegmentKind::NonFinal if plaintext_length == maximum => {
304            Ok(context.parameters.ciphertext_segment_length())
305        }
306        SegmentKind::NonFinal => Err(Error::InvalidPlaintextLength {
307            actual: plaintext_length,
308            required: LengthRequirement::Exactly(maximum),
309        }),
310    }
311}
312
313/// Validates the segment lengths, writes the framing bytes into `output`, and
314/// resolves the segment key. Returns everything the caller's seal needs: the
315/// complete segment length, the fresh nonce, the key, and the segment AAD.
316#[inline]
317fn begin_segment_encryption<'a>(
318    context: &MessageContext,
319    keys: &'a mut KeyCache,
320    nonces: &mut NonceGenerator,
321    output: &mut [u8],
322    plaintext_length: usize,
323    position: u64,
324    kind: SegmentKind,
325) -> Result<(
326    usize,
327    [u8; AEAD_IV_LENGTH],
328    &'a AeadKey,
329    [u8; SEGMENT_AAD_LENGTH],
330)> {
331    let required = ciphertext_segment_size(context, plaintext_length, kind)?;
332    if output.len() < required {
333        return Err(Error::OutputTooSmall {
334            actual: output.len(),
335            required,
336        });
337    }
338    let nonce = write_segment_framing(nonces, output, kind, required)?;
339    let key = keys.key_for_position(context, position)?;
340    Ok((required, nonce, key, segment_aad(position, kind)))
341}
342
343#[inline]
344fn encrypt_segment_into_inner(
345    context: &MessageContext,
346    keys: &mut KeyCache,
347    nonces: &mut NonceGenerator,
348    plaintext: &[u8],
349    position: u64,
350    kind: SegmentKind,
351    output: &mut [u8],
352) -> Result<usize> {
353    let (required, nonce, key, segment_aad) = begin_segment_encryption(
354        context,
355        keys,
356        nonces,
357        output,
358        plaintext.len(),
359        position,
360        kind,
361    )?;
362    key.seal_into(
363        &nonce,
364        &segment_aad,
365        plaintext,
366        &mut output[SEGMENT_PAYLOAD_OFFSET..required],
367    )?;
368    Ok(required)
369}
370
371#[inline]
372fn encrypt_segment_in_place_inner(
373    context: &MessageContext,
374    keys: &mut KeyCache,
375    nonces: &mut NonceGenerator,
376    buffer: &mut [u8],
377    plaintext_length: usize,
378    position: u64,
379    kind: SegmentKind,
380) -> Result<usize> {
381    let (required, nonce, key, segment_aad) = begin_segment_encryption(
382        context,
383        keys,
384        nonces,
385        buffer,
386        plaintext_length,
387        position,
388        kind,
389    )?;
390    let tag_start = SEGMENT_PAYLOAD_OFFSET + plaintext_length;
391    let mut tag = [0u8; AEAD_TAG_LENGTH];
392    key.seal(
393        &nonce,
394        &segment_aad,
395        &mut buffer[SEGMENT_PAYLOAD_OFFSET..tag_start],
396        &mut tag,
397    )?;
398    buffer[tag_start..required].copy_from_slice(&tag);
399    Ok(required)
400}
401
402/// Writes the length prefix and a fresh nonce into the segment's framing
403/// bytes and returns the nonce. The payload region is not touched.
404#[inline]
405fn write_segment_framing(
406    nonces: &mut NonceGenerator,
407    output: &mut [u8],
408    kind: SegmentKind,
409    required: usize,
410) -> Result<[u8; AEAD_IV_LENGTH]> {
411    let prefix = match kind {
412        SegmentKind::NonFinal => u32::MAX,
413        SegmentKind::Final => u32::try_from(required).map_err(|_| Error::LengthOverflow)?,
414    };
415    output[..SEGMENT_PREFIX_LENGTH].copy_from_slice(&prefix.to_be_bytes());
416    let nonce = nonces.next()?;
417    output[SEGMENT_PREFIX_LENGTH..SEGMENT_PAYLOAD_OFFSET].copy_from_slice(&nonce);
418    Ok(nonce)
419}
420
421#[inline]
422fn validate_segment(
423    context: &MessageContext,
424    ciphertext_segment: &[u8],
425    kind: SegmentKind,
426) -> Result<usize> {
427    let maximum = context.parameters.ciphertext_segment_length();
428    match kind {
429        SegmentKind::Final => {
430            context
431                .parameters
432                .validate_ciphertext_segment_length(ciphertext_segment.len())?;
433        }
434        SegmentKind::NonFinal if ciphertext_segment.len() != maximum => {
435            return Err(Error::InvalidCiphertextLength {
436                actual: ciphertext_segment.len(),
437                required: LengthRequirement::Exactly(maximum),
438            });
439        }
440        SegmentKind::NonFinal => {}
441    }
442
443    let prefix = u32::from_be_bytes(
444        ciphertext_segment[..SEGMENT_PREFIX_LENGTH]
445            .try_into()
446            .map_err(|_| Error::InvalidSegmentPrefix)?,
447    );
448
449    match kind {
450        // `prefix` is attacker-controlled, usize might not be 64-bits, so
451        // promote everything to u64 for comparison
452        SegmentKind::Final
453            if u64::from(prefix) != length_usize_to_u64(ciphertext_segment.len()) =>
454        {
455            return Err(Error::InvalidCiphertextLength {
456                actual: ciphertext_segment.len(),
457                required: LengthRequirement::Exactly(length_u32_to_usize(prefix)),
458            });
459        }
460        SegmentKind::NonFinal if prefix != u32::MAX => {
461            return Err(Error::InvalidSegmentPrefix);
462        }
463        SegmentKind::Final | SegmentKind::NonFinal => {}
464    }
465
466    Ok(ciphertext_segment.len() - SEGMENT_OVERHEAD)
467}
468
469#[inline]
470fn decrypt_segment_into_inner(
471    context: &MessageContext,
472    keys: &mut KeyCache,
473    ciphertext_segment: &[u8],
474    position: u64,
475    kind: SegmentKind,
476    plaintext_length: usize,
477    output: &mut [u8],
478) -> Result<usize> {
479    if output.len() < plaintext_length {
480        return Err(Error::OutputTooSmall {
481            actual: output.len(),
482            required: plaintext_length,
483        });
484    }
485
486    let key = keys.key_for_position(context, position)?;
487    let tag_start = ciphertext_segment.len() - AEAD_TAG_LENGTH;
488    let segment_aad = segment_aad(position, kind);
489    let nonce: &[u8; AEAD_IV_LENGTH] = ciphertext_segment
490        [SEGMENT_PREFIX_LENGTH..SEGMENT_PAYLOAD_OFFSET]
491        .try_into()
492        .map_err(|_| Error::CryptoFailure)?;
493    let tag: &[u8; AEAD_TAG_LENGTH] = ciphertext_segment[tag_start..]
494        .try_into()
495        .map_err(|_| Error::CryptoFailure)?;
496
497    key.open(
498        nonce,
499        &segment_aad,
500        &ciphertext_segment[SEGMENT_PAYLOAD_OFFSET..tag_start],
501        tag,
502        &mut output[..plaintext_length],
503    )?;
504    Ok(plaintext_length)
505}
506
507#[inline]
508fn decrypt_segment_in_place_inner<'a>(
509    context: &MessageContext,
510    keys: &mut KeyCache,
511    ciphertext_segment: &'a mut [u8],
512    position: u64,
513    kind: SegmentKind,
514) -> Result<&'a mut [u8]> {
515    let plaintext_length = validate_segment(context, ciphertext_segment, kind)?;
516    open_segment_in_place(
517        context,
518        keys,
519        ciphertext_segment,
520        position,
521        kind,
522        plaintext_length,
523    )
524}
525
526/// Opens a segment whose framing has already been validated (or supplied by
527/// a decoded [`SegmentFraming`] whose length matched the segment).
528#[inline]
529fn open_segment_in_place<'a>(
530    context: &MessageContext,
531    keys: &mut KeyCache,
532    ciphertext_segment: &'a mut [u8],
533    position: u64,
534    kind: SegmentKind,
535    plaintext_length: usize,
536) -> Result<&'a mut [u8]> {
537    let key = keys.key_for_position(context, position)?;
538    let (framing, ciphertext_and_tag) = ciphertext_segment.split_at_mut(SEGMENT_PAYLOAD_OFFSET);
539    let nonce: &[u8; AEAD_IV_LENGTH] = framing[SEGMENT_PREFIX_LENGTH..]
540        .try_into()
541        .map_err(|_| Error::CryptoFailure)?;
542    let (ciphertext, tag_bytes) = ciphertext_and_tag.split_at_mut(plaintext_length);
543    let tag: &[u8; AEAD_TAG_LENGTH] = (&*tag_bytes).try_into().map_err(|_| Error::CryptoFailure)?;
544
545    let segment_aad = segment_aad(position, kind);
546    key.open_in_place(nonce, &segment_aad, tag, ciphertext)?;
547    Ok(ciphertext)
548}
549
550/// Applies an in-place decryption outcome to the buffer's state machine:
551/// authenticated plaintext on success, a zeroized empty buffer on failure so
552/// no unauthenticated payload bytes linger in the reusable storage.
553fn finish_in_place_decrypt(buffer: &mut SegmentBuffer, result: Result<usize>) -> Result<&mut [u8]> {
554    match result {
555        Ok(length) => {
556            buffer.mark_plaintext(length);
557            buffer.plaintext_mut()
558        }
559        Err(error) => {
560            buffer.clear();
561            Err(error)
562        }
563    }
564}
565
566#[inline]
567fn validate_layout(context: &MessageContext, segment: SegmentLayout) -> Result<()> {
568    if segment.parameters() == context.parameters {
569        Ok(())
570    } else {
571        Err(Error::InvalidParameters)
572    }
573}
574
575/// State for the specification's random-access encryption functions.
576///
577/// Use [`Parameters::plaintext_layout`] and pass each resulting
578/// [`SegmentLayout`] directly to the corresponding segment operation.
579///
580/// The caller must encrypt each position at most once, produce exactly one
581/// final segment, leave no gaps, and never encrypt a position beyond the final
582/// segment.
583#[derive(Debug)]
584pub struct EncryptionState {
585    context: Arc<MessageContext>,
586    keys: KeyCache,
587    nonces: NonceGenerator,
588}
589
590impl EncryptionState {
591    /// Returns the provider used by this state.
592    #[must_use]
593    pub fn provider(&self) -> Provider {
594        self.context.provider
595    }
596
597    /// Returns this state's parameter set.
598    #[must_use]
599    pub fn parameters(&self) -> Parameters {
600        self.context.parameters
601    }
602
603    /// Consumes this state and makes its immutable message context shareable
604    /// by independent encryption states.
605    #[must_use]
606    pub fn into_shared(self) -> SharedEncryptionContext {
607        let Self { context, .. } = self;
608        SharedEncryptionContext { context }
609    }
610
611    /// Encrypts one random-access segment into a newly allocated buffer.
612    ///
613    /// # Errors
614    ///
615    /// Returns an error when the layout belongs to another parameter set, the
616    /// plaintext length does not match the layout, the position is invalid, or
617    /// the selected backend cannot generate a nonce or encrypt the segment.
618    pub fn encrypt_segment(&mut self, plaintext: &[u8], segment: SegmentLayout) -> Result<Vec<u8>> {
619        self.validate_plaintext_layout(plaintext.len(), segment)?;
620        self.encrypt_segment_at(plaintext, segment.position(), segment.kind())
621    }
622
623    /// Encrypts one random-access segment into `output`, returning bytes written.
624    ///
625    /// [`SegmentLayout::ciphertext_length`] gives the required
626    /// output length for a segment in a complete message layout.
627    ///
628    /// This method performs no heap allocation on the normal per-segment path.
629    ///
630    /// # Errors
631    ///
632    /// Returns an error for invalid input, insufficient output space, segment
633    /// limit exhaustion, random generation failure, or encryption failure.
634    #[inline]
635    pub fn encrypt_segment_into(
636        &mut self,
637        plaintext: &[u8],
638        segment: SegmentLayout,
639        output: &mut [u8],
640    ) -> Result<usize> {
641        self.validate_plaintext_layout(plaintext.len(), segment)?;
642        self.encrypt_segment_into_at(plaintext, segment.position(), segment.kind(), output)
643    }
644
645    /// Encrypts plaintext prepared in a reusable [`SegmentBuffer`].
646    ///
647    /// Prepare the payload with [`SegmentBuffer::prepare_plaintext`]. On
648    /// success, [`SegmentBuffer::ciphertext`] returns the complete ciphertext
649    /// segment without copying the plaintext.
650    ///
651    /// # Errors
652    ///
653    /// Returns an error for invalid input, insufficient buffer space, segment
654    /// limit exhaustion, random generation failure, or encryption failure.
655    #[inline]
656    pub fn encrypt_segment_in_place<'a>(
657        &mut self,
658        buffer: &'a mut SegmentBuffer,
659        segment: SegmentLayout,
660    ) -> Result<&'a [u8]> {
661        self.validate_plaintext_layout(buffer.plaintext_length()?, segment)?;
662        self.encrypt_segment_in_place_at(buffer, segment.position(), segment.kind())
663    }
664
665    pub(crate) fn encrypt_segment_in_place_at<'a>(
666        &mut self,
667        buffer: &'a mut SegmentBuffer,
668        position: u64,
669        kind: SegmentKind,
670    ) -> Result<&'a [u8]> {
671        if !buffer.matches(self.parameters()) {
672            return Err(Error::InvalidParameters);
673        }
674
675        let plaintext_length = buffer.plaintext_length()?;
676        let result = self.encrypt_segment_in_place_raw_at(
677            buffer.raw_mut(),
678            plaintext_length,
679            position,
680            kind,
681        );
682
683        match result {
684            Ok(written) => {
685                buffer.mark_ciphertext(written);
686                buffer.ciphertext()
687            }
688            Err(error) => {
689                // Zeroize rather than merely discard: the buffer still holds
690                // the plaintext that failed to encrypt.
691                buffer.clear();
692                Err(error)
693            }
694        }
695    }
696
697    /// Encrypts a segment in caller-managed storage.
698    ///
699    /// Before calling, place [`SegmentLayout::plaintext_length`] bytes at
700    /// [`crate::low_level::SEGMENT_PAYLOAD_OFFSET`]. The buffer must be large
701    /// enough for the complete encrypted segment. Prefer
702    /// [`SegmentBuffer`] unless direct pool, stack, or arena integration is
703    /// required.
704    ///
705    /// # Errors
706    ///
707    /// Returns an error for an invalid position or plaintext length,
708    /// insufficient buffer space, segment-limit exhaustion, random-generation
709    /// failure, or encryption failure.
710    pub fn encrypt_segment_in_place_raw(
711        &mut self,
712        buffer: &mut [u8],
713        segment: SegmentLayout,
714    ) -> Result<usize> {
715        validate_layout(&self.context, segment)?;
716        let plaintext_length = segment.plaintext_length();
717
718        self.encrypt_segment_in_place_raw_at(
719            buffer,
720            plaintext_length,
721            segment.position(),
722            segment.kind(),
723        )
724    }
725
726    pub(crate) fn encrypt_segment_at(
727        &mut self,
728        plaintext: &[u8],
729        position: u64,
730        kind: SegmentKind,
731    ) -> Result<Vec<u8>> {
732        let required = ciphertext_segment_size(&self.context, plaintext.len(), kind)?;
733        let mut output = vec![0u8; required];
734        self.encrypt_segment_into_at(plaintext, position, kind, &mut output)?;
735        Ok(output)
736    }
737
738    pub(crate) fn encrypt_segment_into_at(
739        &mut self,
740        plaintext: &[u8],
741        position: u64,
742        kind: SegmentKind,
743        output: &mut [u8],
744    ) -> Result<usize> {
745        encrypt_segment_into_inner(
746            &self.context,
747            &mut self.keys,
748            &mut self.nonces,
749            plaintext,
750            position,
751            kind,
752            output,
753        )
754    }
755
756    pub(crate) fn encrypt_segment_in_place_raw_at(
757        &mut self,
758        buffer: &mut [u8],
759        plaintext_length: usize,
760        position: u64,
761        kind: SegmentKind,
762    ) -> Result<usize> {
763        encrypt_segment_in_place_inner(
764            &self.context,
765            &mut self.keys,
766            &mut self.nonces,
767            buffer,
768            plaintext_length,
769            position,
770            kind,
771        )
772    }
773
774    fn validate_plaintext_layout(&self, actual: usize, segment: SegmentLayout) -> Result<()> {
775        validate_layout(&self.context, segment)?;
776        let expected = segment.plaintext_length();
777
778        if actual == expected {
779            Ok(())
780        } else {
781            Err(Error::InvalidPlaintextLength {
782                actual,
783                required: LengthRequirement::Exactly(expected),
784            })
785        }
786    }
787}
788
789/// Shareable message context for parallel random-access encryption.
790///
791/// Create this by consuming an [`EncryptionState`] with
792/// [`EncryptionState::into_shared`], then create one [`EncryptionState`] for
793/// each independently executing task.
794#[derive(Clone, Debug)]
795pub struct SharedEncryptionContext {
796    context: Arc<MessageContext>,
797}
798
799impl SharedEncryptionContext {
800    /// Returns the provider retained by this context.
801    #[must_use]
802    pub fn provider(&self) -> Provider {
803        self.context.provider
804    }
805
806    /// Returns this context's parameter set.
807    #[must_use]
808    pub fn parameters(&self) -> Parameters {
809        self.context.parameters
810    }
811
812    /// Creates an independent encryption state with its own key cache and
813    /// nonce generator.
814    #[must_use]
815    pub fn fork(&self) -> EncryptionState {
816        EncryptionState {
817            context: Arc::clone(&self.context),
818            keys: KeyCache::empty(),
819            nonces: NonceGenerator::new(ProviderRng::new(self.context.provider)),
820        }
821    }
822}
823
824/// State for the specification's random-access decryption functions.
825///
826/// For a seekable complete ciphertext, [`Parameters::ciphertext_layout`]
827/// supplies a [`SegmentLayout`] that can be passed directly to each operation.
828/// Layout metadata is not authenticated until segment decryption succeeds.
829///
830/// Prefer [`crate::random_access::Reader`] when the input implements `Read + Seek`.
831#[derive(Debug)]
832pub struct DecryptionState {
833    context: Arc<MessageContext>,
834    keys: KeyCache,
835}
836
837impl DecryptionState {
838    /// Returns the provider used by this state.
839    #[must_use]
840    pub fn provider(&self) -> Provider {
841        self.context.provider
842    }
843
844    /// Returns this state's parameter set.
845    #[must_use]
846    pub fn parameters(&self) -> Parameters {
847        self.context.parameters
848    }
849
850    /// Consumes this state and makes its immutable message context shareable
851    /// by independent decryption states.
852    #[must_use]
853    pub fn into_shared(self) -> SharedDecryptionContext {
854        let Self { context, .. } = self;
855        SharedDecryptionContext { context }
856    }
857
858    /// Decrypts one random-access segment into a newly allocated buffer.
859    ///
860    /// # Errors
861    ///
862    /// Returns an error when the layout belongs to another parameter set, the
863    /// encrypted length does not match it, framing, position, or authentication
864    /// validation fails, or the selected backend rejects the segment key.
865    pub fn decrypt_segment(
866        &mut self,
867        ciphertext_segment: &[u8],
868        segment: SegmentLayout,
869    ) -> Result<Vec<u8>> {
870        self.validate_ciphertext_layout(ciphertext_segment.len(), segment)?;
871        self.decrypt_segment_at(ciphertext_segment, segment.position(), segment.kind())
872    }
873
874    /// Decrypts one random-access segment into `output`, returning bytes written.
875    ///
876    /// [`SegmentLayout::plaintext_length`] gives the required output length.
877    ///
878    /// Authentication failure may overwrite bytes in `output`; callers must not
879    /// use its contents unless this method succeeds.
880    ///
881    /// # Errors
882    ///
883    /// Returns an error for malformed input, insufficient output space, an
884    /// invalid position, key derivation failure, or authentication failure.
885    #[inline]
886    pub fn decrypt_segment_into(
887        &mut self,
888        ciphertext_segment: &[u8],
889        segment: SegmentLayout,
890        output: &mut [u8],
891    ) -> Result<usize> {
892        self.validate_ciphertext_layout(ciphertext_segment.len(), segment)?;
893        self.decrypt_segment_into_at(
894            ciphertext_segment,
895            segment.position(),
896            segment.kind(),
897            output,
898        )
899    }
900
901    /// Authenticates and decrypts a segment in a reusable [`SegmentBuffer`].
902    ///
903    /// Prepare the ciphertext with [`SegmentBuffer::prepare_ciphertext`]. On
904    /// success, this returns the authenticated plaintext within the buffer.
905    ///
906    /// Failure, including authentication failure, zeroizes the buffer and
907    /// leaves it empty.
908    ///
909    /// # Errors
910    ///
911    /// Returns an error for malformed input, an invalid position, key
912    /// derivation failure, or authentication failure.
913    #[inline]
914    pub fn decrypt_segment_in_place<'a>(
915        &mut self,
916        buffer: &'a mut SegmentBuffer,
917        segment: SegmentLayout,
918    ) -> Result<&'a mut [u8]> {
919        self.validate_ciphertext_layout(buffer.ciphertext_length()?, segment)?;
920        self.decrypt_segment_in_place_at(buffer, segment.position(), segment.kind())
921    }
922
923    pub(crate) fn decrypt_segment_in_place_at<'a>(
924        &mut self,
925        buffer: &'a mut SegmentBuffer,
926        position: u64,
927        kind: SegmentKind,
928    ) -> Result<&'a mut [u8]> {
929        if !buffer.matches(self.parameters()) {
930            return Err(Error::InvalidParameters);
931        }
932        let ciphertext_length = buffer.ciphertext_length()?;
933        let result = self
934            .decrypt_segment_in_place_raw_at(
935                &mut buffer.raw_mut()[..ciphertext_length],
936                position,
937                kind,
938            )
939            .map(|plaintext| plaintext.len());
940        finish_in_place_decrypt(buffer, result)
941    }
942
943    /// In-place decryption for a segment whose framing prefix was already
944    /// decoded, so the prefix is not parsed and validated a second time.
945    pub(crate) fn decrypt_segment_in_place_at_framed<'a>(
946        &mut self,
947        buffer: &'a mut SegmentBuffer,
948        position: u64,
949        framing: SegmentFraming,
950    ) -> Result<&'a mut [u8]> {
951        if !buffer.matches(self.parameters()) {
952            return Err(Error::InvalidParameters);
953        }
954        let ciphertext_length = buffer.ciphertext_length()?;
955        let expected = framing.ciphertext_length();
956        if ciphertext_length != expected {
957            return Err(Error::InvalidCiphertextLength {
958                actual: ciphertext_length,
959                required: LengthRequirement::Exactly(expected),
960            });
961        }
962        let result = open_segment_in_place(
963            &self.context,
964            &mut self.keys,
965            &mut buffer.raw_mut()[..ciphertext_length],
966            position,
967            framing.kind(),
968            framing.plaintext_length(),
969        )
970        .map(|plaintext| plaintext.len());
971        finish_in_place_decrypt(buffer, result)
972    }
973
974    /// Authenticates and decrypts a segment in caller-managed storage.
975    ///
976    /// On success, the returned slice identifies the authenticated plaintext
977    /// within `ciphertext_segment`. Prefer [`SegmentBuffer`] unless direct pool,
978    /// stack, or arena integration is required.
979    ///
980    /// Authentication failure may overwrite the ciphertext payload. Callers
981    /// must not use the buffer contents unless this method succeeds.
982    ///
983    /// # Errors
984    ///
985    /// Returns an error for malformed framing, an invalid position, key
986    /// derivation failure, or authentication failure.
987    pub fn decrypt_segment_in_place_raw<'a>(
988        &mut self,
989        ciphertext_segment: &'a mut [u8],
990        segment: SegmentLayout,
991    ) -> Result<&'a mut [u8]> {
992        validate_layout(&self.context, segment)?;
993        let required = segment.ciphertext_length();
994        if ciphertext_segment.len() < required {
995            return Err(Error::InvalidCiphertextLength {
996                actual: ciphertext_segment.len(),
997                required: LengthRequirement::AtLeast(required),
998            });
999        }
1000        self.decrypt_segment_in_place_raw_at(
1001            &mut ciphertext_segment[..required],
1002            segment.position(),
1003            segment.kind(),
1004        )
1005    }
1006
1007    pub(crate) fn decrypt_segment_at(
1008        &mut self,
1009        ciphertext_segment: &[u8],
1010        position: u64,
1011        kind: SegmentKind,
1012    ) -> Result<Vec<u8>> {
1013        let plaintext_length = validate_segment(&self.context, ciphertext_segment, kind)?;
1014        // Zeroizing wipes any partially written plaintext if a later step
1015        // fails; success moves the vector out and drops an empty guard.
1016        let mut output = Zeroizing::new(vec![0u8; plaintext_length]);
1017        decrypt_segment_into_inner(
1018            &self.context,
1019            &mut self.keys,
1020            ciphertext_segment,
1021            position,
1022            kind,
1023            plaintext_length,
1024            &mut output,
1025        )?;
1026        Ok(core::mem::take(&mut *output))
1027    }
1028
1029    /// Allocating decryption for a segment whose framing prefix was already
1030    /// decoded, so the prefix is not parsed and validated a second time.
1031    pub(crate) fn decrypt_segment_at_framed(
1032        &mut self,
1033        ciphertext_segment: &[u8],
1034        position: u64,
1035        framing: SegmentFraming,
1036    ) -> Result<Vec<u8>> {
1037        let mut output = Zeroizing::new(vec![0u8; framing.plaintext_length()]);
1038        self.decrypt_segment_into_at_framed(ciphertext_segment, position, framing, &mut output)?;
1039        Ok(core::mem::take(&mut *output))
1040    }
1041
1042    pub(crate) fn decrypt_segment_into_at(
1043        &mut self,
1044        ciphertext_segment: &[u8],
1045        position: u64,
1046        kind: SegmentKind,
1047        output: &mut [u8],
1048    ) -> Result<usize> {
1049        let plaintext_length = validate_segment(&self.context, ciphertext_segment, kind)?;
1050        decrypt_segment_into_inner(
1051            &self.context,
1052            &mut self.keys,
1053            ciphertext_segment,
1054            position,
1055            kind,
1056            plaintext_length,
1057            output,
1058        )
1059    }
1060
1061    pub(crate) fn decrypt_segment_into_at_framed(
1062        &mut self,
1063        ciphertext_segment: &[u8],
1064        position: u64,
1065        framing: SegmentFraming,
1066        output: &mut [u8],
1067    ) -> Result<usize> {
1068        let expected = framing.ciphertext_length();
1069        if ciphertext_segment.len() != expected {
1070            return Err(Error::InvalidCiphertextLength {
1071                actual: ciphertext_segment.len(),
1072                required: LengthRequirement::Exactly(expected),
1073            });
1074        }
1075        decrypt_segment_into_inner(
1076            &self.context,
1077            &mut self.keys,
1078            ciphertext_segment,
1079            position,
1080            framing.kind(),
1081            framing.plaintext_length(),
1082            output,
1083        )
1084    }
1085
1086    pub(crate) fn decrypt_segment_in_place_raw_at<'a>(
1087        &mut self,
1088        ciphertext_segment: &'a mut [u8],
1089        position: u64,
1090        kind: SegmentKind,
1091    ) -> Result<&'a mut [u8]> {
1092        decrypt_segment_in_place_inner(
1093            &self.context,
1094            &mut self.keys,
1095            ciphertext_segment,
1096            position,
1097            kind,
1098        )
1099    }
1100
1101    fn validate_ciphertext_layout(&self, actual: usize, segment: SegmentLayout) -> Result<()> {
1102        validate_layout(&self.context, segment)?;
1103        let expected = segment.ciphertext_length();
1104        if actual == expected {
1105            Ok(())
1106        } else {
1107            Err(Error::InvalidCiphertextLength {
1108                actual,
1109                required: LengthRequirement::Exactly(expected),
1110            })
1111        }
1112    }
1113}
1114
1115/// Shareable message context for parallel/concurrent random-access decryption.
1116///
1117/// Create this by consuming a [`DecryptionState`] with
1118/// [`DecryptionState::into_shared`], then create one [`DecryptionState`] for
1119/// each independently executing task/thread.
1120#[derive(Clone, Debug)]
1121pub struct SharedDecryptionContext {
1122    context: Arc<MessageContext>,
1123}
1124
1125impl SharedDecryptionContext {
1126    /// Returns the provider retained by this context.
1127    #[must_use]
1128    pub fn provider(&self) -> Provider {
1129        self.context.provider
1130    }
1131
1132    /// Returns this context's parameter set.
1133    #[must_use]
1134    pub fn parameters(&self) -> Parameters {
1135        self.context.parameters
1136    }
1137
1138    /// Creates an independent decryption state with its own key cache.
1139    #[must_use]
1140    pub fn fork(&self) -> DecryptionState {
1141        DecryptionState {
1142            context: Arc::clone(&self.context),
1143            keys: KeyCache::empty(),
1144        }
1145    }
1146}
1147
1148/// Initialize a FLOE encryption state.
1149///
1150/// # Errors
1151///
1152/// Returns an error if a provider is required or the selected provider cannot
1153/// initialize or generate the FLOE IV.
1154pub fn start_encryption(
1155    key: &Key,
1156    aad: &[u8],
1157    parameters: Parameters,
1158) -> Result<(EncryptionState, Header)> {
1159    let provider = key.provider()?;
1160    let mut rng = ProviderRng::new(provider);
1161    let mut floe_iv = [0u8; FLOE_IV_LENGTH];
1162    rng.fill(&mut floe_iv)?;
1163
1164    let encoded = parameters.encode();
1165    let mut header_tag = derive_header_tag(provider, key, &encoded, &floe_iv, aad)?;
1166    let message_key = derive_message_key(provider, key, &encoded, &floe_iv, aad)?;
1167
1168    let header = Header::from_fields(&encoded, &floe_iv, &header_tag);
1169    header_tag.zeroize();
1170
1171    let context = Arc::new(MessageContext::new(
1172        provider,
1173        parameters,
1174        message_key,
1175        floe_iv,
1176        aad,
1177    ));
1178    let keys = KeyCache::new(&context)?;
1179    let nonces = NonceGenerator::new(rng);
1180
1181    Ok((
1182        EncryptionState {
1183            context,
1184            keys,
1185            nonces,
1186        },
1187        header,
1188    ))
1189}
1190
1191/// Starts decryption using the provided parameter set and header.
1192///
1193/// # Errors
1194///
1195/// Returns an error if the encoded parameters or header is invalid,
1196/// if the provided parameters don't match the parameters in the header,
1197/// or if an explicit provider is required.
1198pub fn start_decryption(
1199    key: &Key,
1200    aad: &[u8],
1201    parameters: Parameters,
1202    header: &Header,
1203) -> Result<DecryptionState> {
1204    let provider = key.provider()?;
1205    start_decryption_with_provider(key, provider, aad, parameters, header)
1206}
1207
1208fn start_decryption_with_provider(
1209    key: &Key,
1210    provider: Provider,
1211    aad: &[u8],
1212    parameters: Parameters,
1213    header: &Header,
1214) -> Result<DecryptionState> {
1215    let encoded = parameters.encode();
1216
1217    if header.encoded_parameters() != &encoded {
1218        return Err(Error::InvalidHeaderParameters);
1219    }
1220
1221    let floe_iv = *header.floe_iv();
1222
1223    let mut expected_tag = derive_header_tag(provider, key, &encoded, &floe_iv, aad)?;
1224    let tag_matches: bool = expected_tag.as_slice().ct_eq(header.tag()).into();
1225    expected_tag.zeroize();
1226
1227    if !tag_matches {
1228        return Err(Error::InvalidHeaderTag);
1229    }
1230
1231    let message_key = derive_message_key(provider, key, &encoded, &floe_iv, aad)?;
1232    let context = Arc::new(MessageContext::new(
1233        provider,
1234        parameters,
1235        message_key,
1236        floe_iv,
1237        aad,
1238    ));
1239    let keys = KeyCache::new(&context)?;
1240
1241    Ok(DecryptionState { context, keys })
1242}
1243
1244/// Starts decryption using the parameter set in `header`.
1245///
1246/// Use [`start_decryption`] to provide the parameters explicitly.
1247///
1248/// # Errors
1249///
1250/// Returns an error if the parameters or header are invalid, or
1251/// if an explicit provider is required.
1252pub fn start_decryption_inferred(
1253    key: &Key,
1254    aad: &[u8],
1255    header: &Header,
1256) -> Result<DecryptionState> {
1257    let provider = key.provider()?;
1258    let parameters = header.unverified_parameters()?;
1259    start_decryption_with_provider(key, provider, aad, parameters, header)
1260}
1261
1262fn derive_header_tag(
1263    provider: Provider,
1264    key: &Key,
1265    encoded: &[u8; ENCODED_PARAMETERS_LENGTH],
1266    floe_iv: &[u8; FLOE_IV_LENGTH],
1267    aad: &[u8],
1268) -> Result<[u8; HEADER_TAG_LENGTH]> {
1269    let info: [&[u8]; 4] = [encoded, floe_iv, HEADER_TAG_PURPOSE, aad];
1270    provider.kdf_expand::<HEADER_TAG_LENGTH>(key.as_bytes(), &info)
1271}
1272
1273fn derive_message_key(
1274    provider: Provider,
1275    key: &Key,
1276    encoded: &[u8; ENCODED_PARAMETERS_LENGTH],
1277    floe_iv: &[u8; FLOE_IV_LENGTH],
1278    aad: &[u8],
1279) -> Result<SecretBytes<48>> {
1280    let info: [&[u8]; 4] = [encoded, floe_iv, MESSAGE_KEY_PURPOSE, aad];
1281    Ok(SecretBytes(
1282        provider.kdf_expand::<48>(key.as_bytes(), &info)?,
1283    ))
1284}
1285
1286fn derive_segment_key(
1287    provider: Provider,
1288    parameters: Parameters,
1289    message_key: &SecretBytes<48>,
1290    floe_iv: &[u8; FLOE_IV_LENGTH],
1291    aad: &[u8],
1292    masked_position: u64,
1293) -> Result<AeadKey> {
1294    let encoded = parameters.encode();
1295    let position_bytes = masked_position.to_be_bytes();
1296    let info: [&[u8]; 5] = [&encoded, floe_iv, SEGMENT_KEY_PURPOSE, &position_bytes, aad];
1297    let mut key_material = provider.kdf_expand::<32>(message_key.expose_bytes(), &info)?;
1298    let key = AeadKey::new(provider, &key_material);
1299    key_material.zeroize();
1300    key
1301}
1302
1303#[cfg(test)]
1304mod tests {
1305    use std::collections::HashSet;
1306
1307    use rayon::prelude::*;
1308
1309    use super::*;
1310    use crate::key::test_key;
1311    use crate::{decrypt, encrypt};
1312
1313    #[test]
1314    fn inferred_decryption_uses_header_declared_parameters() {
1315        // Given a ciphertext whose header declares 1 MiB segments
1316        let plaintext = b"header-selected parameters";
1317        let ciphertext = encrypt(
1318            &test_key(),
1319            b"header inference",
1320            Parameters::SEGMENT_1_MIB,
1321            plaintext,
1322        )
1323        .unwrap();
1324
1325        // When the header is parsed from the ciphertext prefix
1326        let header = Header::try_from(&ciphertext[..Header::LEN]).unwrap();
1327
1328        // Then the typed header has the specified length and exposes the
1329        // declared parameters before authentication
1330        assert_eq!(Header::LEN, HEADER_LENGTH);
1331        assert_eq!(
1332            header.unverified_parameters().unwrap(),
1333            Parameters::SEGMENT_1_MIB
1334        );
1335
1336        // Then inferred decryption adopts those parameters and the message
1337        // decrypts
1338        assert_eq!(
1339            start_decryption_inferred(&test_key(), b"header inference", &header)
1340                .unwrap()
1341                .parameters(),
1342            Parameters::SEGMENT_1_MIB
1343        );
1344        assert_eq!(
1345            decrypt(&test_key(), b"header inference", &ciphertext).unwrap(),
1346            plaintext
1347        );
1348    }
1349
1350    #[test]
1351    fn tampered_header_parameters_fail_header_authentication() {
1352        // Given a valid ciphertext whose encoded parameters are overwritten
1353        // with a different, individually valid parameter set
1354        let ciphertext = encrypt(
1355            &test_key(),
1356            b"header inference",
1357            Parameters::SEGMENT_1_MIB,
1358            b"header-selected parameters",
1359        )
1360        .unwrap();
1361        let mut changed_parameters = ciphertext.clone();
1362        changed_parameters[..ENCODED_PARAMETERS_LENGTH]
1363            .copy_from_slice(&Parameters::SEGMENT_4_KIB.encode());
1364
1365        // When the tampered header is parsed
1366        let changed_header = Header::try_from(&changed_parameters[..Header::LEN]).unwrap();
1367
1368        // Then the unauthenticated view reports the tampered parameters,
1369        // but decryption rejects the header tag
1370        assert_eq!(
1371            changed_header.unverified_parameters().unwrap(),
1372            Parameters::SEGMENT_4_KIB
1373        );
1374        assert_eq!(
1375            decrypt(&test_key(), b"header inference", &changed_parameters),
1376            Err(Error::InvalidHeaderTag)
1377        );
1378    }
1379
1380    #[test]
1381    fn unsupported_header_parameters_rejected_before_authentication() {
1382        // Given a header whose parameter encoding declares an unsupported
1383        // profile
1384        let (_, header) =
1385            start_encryption(&test_key(), b"header inference", Parameters::SEGMENT_1_MIB).unwrap();
1386        let mut unsupported = <[u8; Header::LEN]>::from(header);
1387        unsupported[0] = 1;
1388        let unsupported = Header::from(unsupported);
1389
1390        // When the parameters are decoded or inferred decryption starts
1391        // Then both reject the encoded parameters
1392        assert_eq!(
1393            unsupported.unverified_parameters(),
1394            Err(Error::InvalidHeaderParameters)
1395        );
1396        assert!(matches!(
1397            start_decryption_inferred(&test_key(), b"header inference", &unsupported),
1398            Err(Error::InvalidHeaderParameters)
1399        ));
1400    }
1401
1402    #[test]
1403    fn final_segment_prefix_must_equal_actual_segment_length() {
1404        // This drives `decrypt_segment_at` directly because
1405        // `SegmentFraming::decode` would reject the prefix before
1406        // `validate_segment` is exercised on the online path.
1407
1408        // Given a valid final segment whose length prefix is forged to
1409        // declare an extra high bit
1410        let parameters = Parameters::SEGMENT_4_KIB;
1411        let (mut encryption, header) =
1412            start_encryption(&test_key(), b"forged final prefix", parameters).unwrap();
1413        let layout = parameters.plaintext_layout(4).unwrap();
1414        let mut segment = encryption
1415            .encrypt_segment(b"last", layout.final_segment())
1416            .unwrap();
1417        let true_length = u32::try_from(segment.len()).unwrap();
1418        let forged = true_length | 0x0001_0000;
1419        segment[..SEGMENT_PREFIX_LENGTH].copy_from_slice(&forged.to_be_bytes());
1420
1421        // When the segment is decrypted at its position
1422        let mut decryption =
1423            start_decryption(&test_key(), b"forged final prefix", parameters, &header).unwrap();
1424        let error = decryption
1425            .decrypt_segment_at(&segment, 0, SegmentKind::Final)
1426            .unwrap_err();
1427
1428        // Then the mismatch between declared and actual length is rejected
1429        assert!(matches!(
1430            error,
1431            Error::InvalidCiphertextLength {
1432                actual,
1433                required: LengthRequirement::Exactly(required),
1434            } if actual == segment.len() && required == segment.len() + 0x1_0000
1435        ));
1436    }
1437
1438    #[test]
1439    fn random_access_segments_decrypt_out_of_order() {
1440        // Given a two-segment message encrypted segment by segment
1441        let parameters = Parameters::SEGMENT_4_KIB;
1442        let full = vec![0x5a; parameters.plaintext_segment_length()];
1443        let final_plaintext = b"final";
1444        let layout = parameters
1445            .plaintext_layout(u64::try_from(full.len() + final_plaintext.len()).unwrap())
1446            .unwrap();
1447        let segment_zero_layout = layout.segment_for_position(0).unwrap();
1448        let segment_one_layout = layout.segment_for_position(1).unwrap();
1449        let (mut encryption, header) =
1450            start_encryption(&test_key(), b"random access", parameters).unwrap();
1451        let segment_zero = encryption
1452            .encrypt_segment(&full, segment_zero_layout)
1453            .unwrap();
1454        let segment_one = encryption
1455            .encrypt_segment(final_plaintext, segment_one_layout)
1456            .unwrap();
1457
1458        // When the segments are decrypted in reverse order
1459        let mut decryption =
1460            start_decryption(&test_key(), b"random access", parameters, &header).unwrap();
1461
1462        // Then each decrypts independently of processing order
1463        assert_eq!(
1464            decryption
1465                .decrypt_segment(&segment_one, segment_one_layout)
1466                .unwrap(),
1467            final_plaintext
1468        );
1469        assert_eq!(
1470            decryption
1471                .decrypt_segment(&segment_zero, segment_zero_layout)
1472                .unwrap(),
1473            full
1474        );
1475    }
1476
1477    #[test]
1478    fn failed_in_place_operations_zeroize_buffer() {
1479        // Given a valid single-segment message encrypted through a reusable
1480        // buffer
1481        let parameters = Parameters::SEGMENT_4_KIB;
1482        let (mut encryption, header) =
1483            start_encryption(&test_key(), b"buffer wipe", parameters).unwrap();
1484        let layout = parameters.plaintext_layout(5).unwrap();
1485        let mut buffer = SegmentBuffer::new(parameters);
1486        buffer
1487            .prepare_plaintext(5)
1488            .unwrap()
1489            .copy_from_slice(b"hello");
1490        let mut segment = encryption
1491            .encrypt_segment_in_place(&mut buffer, layout.final_segment())
1492            .unwrap()
1493            .to_vec();
1494
1495        // When the segment is tampered with and decryption fails
1496        let last = segment.len() - 1;
1497        segment[last] ^= 1;
1498        let mut decryption =
1499            start_decryption(&test_key(), b"buffer wipe", parameters, &header).unwrap();
1500        buffer
1501            .prepare_ciphertext(segment.len())
1502            .unwrap()
1503            .copy_from_slice(&segment);
1504        assert_eq!(
1505            decryption
1506                .decrypt_segment_in_place(&mut buffer, layout.final_segment())
1507                .unwrap_err(),
1508            Error::AuthenticationFailed
1509        );
1510
1511        // Then no payload bytes linger anywhere in the reusable storage
1512        assert!(buffer.raw_mut().iter().all(|&byte| byte == 0));
1513        assert_eq!(buffer.plaintext(), Err(Error::InvalidBufferState));
1514        assert_eq!(buffer.ciphertext(), Err(Error::InvalidBufferState));
1515
1516        // When encryption fails after plaintext was staged in the buffer
1517        buffer
1518            .prepare_plaintext(5)
1519            .unwrap()
1520            .copy_from_slice(b"hello");
1521        assert_eq!(
1522            encryption
1523                .encrypt_segment_in_place_at(&mut buffer, AEAD_MAX_SEGMENTS, SegmentKind::Final)
1524                .unwrap_err(),
1525            Error::SegmentLimit
1526        );
1527
1528        // Then the staged plaintext is wiped as well
1529        assert!(buffer.raw_mut().iter().all(|&byte| byte == 0));
1530    }
1531
1532    #[test]
1533    fn states_and_shared_contexts_cross_thread_boundaries() {
1534        // Given the parallel-processing state and context types
1535        fn assert_send<T: Send>() {}
1536        fn assert_sync<T: Sync>() {}
1537
1538        // Then states move between threads and shared contexts are also
1539        // safe to reference concurrently
1540        assert_send::<EncryptionState>();
1541        assert_send::<DecryptionState>();
1542        assert_send::<SharedEncryptionContext>();
1543        assert_sync::<SharedEncryptionContext>();
1544        assert_send::<SharedDecryptionContext>();
1545        assert_sync::<SharedDecryptionContext>();
1546    }
1547
1548    #[test]
1549    fn parallel_contexts_create_independent_states() {
1550        // Given an eight-segment message split across a thread pool
1551        let parameters = Parameters::SEGMENT_4_KIB;
1552        let segment_length = parameters.plaintext_segment_length();
1553        let plaintext_segments: Vec<Vec<u8>> = (0..8)
1554            .map(|position| {
1555                let length = if position == 7 { 17 } else { segment_length };
1556                vec![u8::try_from(position).unwrap(); length]
1557            })
1558            .collect();
1559        let layout = parameters
1560            .plaintext_layout(
1561                plaintext_segments
1562                    .iter()
1563                    .map(|segment| u64::try_from(segment.len()).unwrap())
1564                    .sum(),
1565            )
1566            .unwrap();
1567
1568        // When every segment is encrypted on its own forked state
1569        let (encryption, header) =
1570            start_encryption(&test_key(), b"parallel states", parameters).unwrap();
1571        let encryption = encryption.into_shared();
1572        assert_eq!(encryption.parameters(), parameters);
1573        let encrypted_segments: Vec<Vec<u8>> = plaintext_segments
1574            .par_iter()
1575            .enumerate()
1576            .map_init(
1577                || encryption.fork(),
1578                |state, (position, plaintext)| {
1579                    assert_eq!(state.parameters(), parameters);
1580                    let segment = layout
1581                        .segment_for_position(u64::try_from(position).unwrap())
1582                        .unwrap();
1583                    state.encrypt_segment(plaintext, segment)
1584                },
1585            )
1586            .collect::<crate::Result<_>>()
1587            .unwrap();
1588
1589        // When every encrypted segment is decrypted on its own forked state
1590        let decryption =
1591            start_decryption(&test_key(), b"parallel states", parameters, &header).unwrap();
1592        let decryption = decryption.into_shared();
1593        assert_eq!(decryption.parameters(), parameters);
1594        let decrypted_segments: Vec<Vec<u8>> = encrypted_segments
1595            .par_iter()
1596            .enumerate()
1597            .map_init(
1598                || decryption.fork(),
1599                |state, (position, encrypted)| {
1600                    assert_eq!(state.parameters(), parameters);
1601                    let segment = layout
1602                        .segment_for_position(u64::try_from(position).unwrap())
1603                        .unwrap();
1604                    state.decrypt_segment(encrypted, segment)
1605                },
1606            )
1607            .collect::<crate::Result<_>>()
1608            .unwrap();
1609
1610        // Then the reassembled plaintext matches the original
1611        assert_eq!(decrypted_segments, plaintext_segments);
1612    }
1613
1614    #[test]
1615    fn rotation_and_position_boundaries_match_specification() {
1616        const ROTATION_INTERVAL: u64 = 1 << 20;
1617
1618        // Given segments encrypted on both sides of a key-rotation boundary
1619        // and at the last permitted position
1620        let parameters = Parameters::SEGMENT_4_KIB;
1621        let (mut encryption, header) =
1622            start_encryption(&test_key(), b"position boundaries", parameters).unwrap();
1623        let before_rotation = encryption
1624            .encrypt_segment_at(b"before", ROTATION_INTERVAL - 1, SegmentKind::Final)
1625            .unwrap();
1626        let after_rotation = encryption
1627            .encrypt_segment_at(b"after", ROTATION_INTERVAL, SegmentKind::Final)
1628            .unwrap();
1629        let last_position = encryption
1630            .encrypt_segment_at(b"last", AEAD_MAX_SEGMENTS - 1, SegmentKind::Final)
1631            .unwrap();
1632
1633        // Then encryption past the segment limit is rejected
1634        assert_eq!(
1635            encryption.encrypt_segment_at(b"past", AEAD_MAX_SEGMENTS, SegmentKind::Final),
1636            Err(Error::SegmentLimit)
1637        );
1638
1639        // When each boundary segment is decrypted at its position
1640        let mut decryption =
1641            start_decryption(&test_key(), b"position boundaries", parameters, &header).unwrap();
1642
1643        // Then each round-trips across the rotation and limit boundaries,
1644        // and decryption past the segment limit is rejected
1645        assert_eq!(
1646            decryption
1647                .decrypt_segment_at(&before_rotation, ROTATION_INTERVAL - 1, SegmentKind::Final,)
1648                .unwrap(),
1649            b"before"
1650        );
1651        assert_eq!(
1652            decryption
1653                .decrypt_segment_at(&after_rotation, ROTATION_INTERVAL, SegmentKind::Final)
1654                .unwrap(),
1655            b"after"
1656        );
1657        assert_eq!(
1658            decryption
1659                .decrypt_segment_at(&last_position, AEAD_MAX_SEGMENTS - 1, SegmentKind::Final,)
1660                .unwrap(),
1661            b"last"
1662        );
1663        assert_eq!(
1664            decryption.decrypt_segment_at(&last_position, AEAD_MAX_SEGMENTS, SegmentKind::Final,),
1665            Err(Error::SegmentLimit)
1666        );
1667    }
1668
1669    #[test]
1670    fn batched_nonces_remain_unique_across_refills() {
1671        // Given more segments than one 64-nonce batch covers
1672        let parameters = Parameters::SEGMENT_4_KIB;
1673        let plaintext = vec![0x5a; parameters.plaintext_segment_length()];
1674        let mut encrypted = vec![0u8; parameters.ciphertext_segment_length()];
1675        let (mut encryption, _) =
1676            start_encryption(&test_key(), b"nonce batches", parameters).unwrap();
1677        let mut nonces = HashSet::new();
1678
1679        let positions = u64::try_from(2 * NONCE_BATCH_SIZE + 2).unwrap();
1680        for position in 0..positions {
1681            // When each segment is encrypted
1682            encryption
1683                .encrypt_segment_into_at(
1684                    &plaintext,
1685                    position,
1686                    SegmentKind::NonFinal,
1687                    &mut encrypted,
1688                )
1689                .unwrap();
1690
1691            // Then the nonce embedded in the segment has never been used
1692            let nonce: [u8; AEAD_IV_LENGTH] = encrypted
1693                [SEGMENT_PREFIX_LENGTH..SEGMENT_PREFIX_LENGTH + AEAD_IV_LENGTH]
1694                .try_into()
1695                .unwrap();
1696            assert!(
1697                nonces.insert(nonce),
1698                "nonce repeated at position {position}"
1699            );
1700        }
1701    }
1702
1703    #[test]
1704    fn segment_layouts_from_other_parameter_sets_rejected() {
1705        // Given an encryption state and a segment layout calculated with a
1706        // different parameter set
1707        let parameters = Parameters::SEGMENT_4_KIB;
1708        let (mut encryption, _) = start_encryption(&test_key(), b"", parameters).unwrap();
1709        let wrong_profile_segment = Parameters::SEGMENT_1_MIB
1710            .plaintext_layout(3)
1711            .unwrap()
1712            .segment_for_position(0)
1713            .unwrap();
1714
1715        // When a segment is encrypted with the mismatched layout
1716        // Then the layout is rejected
1717        assert_eq!(
1718            encryption.encrypt_segment(b"abc", wrong_profile_segment),
1719            Err(Error::InvalidParameters)
1720        );
1721    }
1722
1723    #[test]
1724    fn plaintext_length_must_match_segment_layout() {
1725        // Given a segment layout describing exactly three plaintext bytes
1726        let parameters = Parameters::SEGMENT_4_KIB;
1727        let segment = parameters
1728            .plaintext_layout(3)
1729            .unwrap()
1730            .segment_for_position(0)
1731            .unwrap();
1732        let (mut encryption, _) = start_encryption(&test_key(), b"", parameters).unwrap();
1733
1734        // When two bytes are encrypted against it
1735        // Then the length mismatch is rejected
1736        assert_eq!(
1737            encryption.encrypt_segment(b"ab", segment),
1738            Err(Error::InvalidPlaintextLength {
1739                actual: 2,
1740                required: LengthRequirement::Exactly(3),
1741            })
1742        );
1743    }
1744
1745    #[test]
1746    fn undersized_output_buffers_rejected_before_encryption() {
1747        // Given an output buffer one byte smaller than the segment needs
1748        let parameters = Parameters::SEGMENT_4_KIB;
1749        let segment = parameters
1750            .plaintext_layout(3)
1751            .unwrap()
1752            .segment_for_position(0)
1753            .unwrap();
1754        let (mut encryption, _) = start_encryption(&test_key(), b"", parameters).unwrap();
1755        let mut too_small = [0u8; SEGMENT_OVERHEAD + 2];
1756
1757        // When the segment is encrypted into it
1758        // Then the buffer is rejected
1759        assert!(matches!(
1760            encryption.encrypt_segment_into(
1761                b"abc",
1762                segment,
1763                &mut too_small[..SEGMENT_OVERHEAD + 2]
1764            ),
1765            Err(Error::OutputTooSmall { .. })
1766        ));
1767    }
1768
1769    #[test]
1770    fn into_apis_write_exact_segment_lengths() {
1771        // Given exactly sized caller-provided buffers on both sides
1772        let parameters = Parameters::SEGMENT_4_KIB;
1773        let segment = parameters
1774            .plaintext_layout(3)
1775            .unwrap()
1776            .segment_for_position(0)
1777            .unwrap();
1778        let (mut encryption, header) = start_encryption(&test_key(), b"", parameters).unwrap();
1779
1780        // When the segment is encrypted and decrypted through the into APIs
1781        let mut encrypted = [0u8; SEGMENT_OVERHEAD + 3];
1782        let encrypted_length = encryption
1783            .encrypt_segment_into(b"abc", segment, &mut encrypted)
1784            .unwrap();
1785
1786        // Then each call writes exactly the declared length and the
1787        // plaintext round-trips
1788        assert_eq!(encrypted_length, SEGMENT_OVERHEAD + 3);
1789        let mut decryption = start_decryption(&test_key(), b"", parameters, &header).unwrap();
1790        let mut plaintext = [0u8; 3];
1791        assert_eq!(
1792            decryption
1793                .decrypt_segment_into(&encrypted[..encrypted_length], segment, &mut plaintext,)
1794                .unwrap(),
1795            3
1796        );
1797        assert_eq!(&plaintext, b"abc");
1798    }
1799
1800    #[test]
1801    fn segment_buffers_round_trip_in_place() {
1802        // Given plaintext prepared in a reusable segment buffer
1803        let parameters = Parameters::SEGMENT_4_KIB;
1804        let segment = parameters
1805            .plaintext_layout(3)
1806            .unwrap()
1807            .segment_for_position(0)
1808            .unwrap();
1809        let (mut encryption, header) = start_encryption(&test_key(), b"", parameters).unwrap();
1810        let mut in_place = SegmentBuffer::new(parameters);
1811        in_place
1812            .prepare_plaintext(3)
1813            .unwrap()
1814            .copy_from_slice(b"abc");
1815
1816        // When the buffer is encrypted and decrypted in place
1817        assert_eq!(
1818            encryption
1819                .encrypt_segment_in_place(&mut in_place, segment)
1820                .unwrap()
1821                .len(),
1822            SEGMENT_OVERHEAD + 3
1823        );
1824        let mut decryption = start_decryption(&test_key(), b"", parameters, &header).unwrap();
1825
1826        // Then the original plaintext is recovered without copying
1827        assert_eq!(
1828            decryption
1829                .decrypt_segment_in_place(&mut in_place, segment)
1830                .unwrap(),
1831            b"abc"
1832        );
1833    }
1834
1835    #[test]
1836    fn in_place_decryption_rejects_tampered_ciphertext() {
1837        // Given an in-place encrypted segment whose last byte is flipped
1838        let parameters = Parameters::SEGMENT_4_KIB;
1839        let segment = parameters
1840            .plaintext_layout(3)
1841            .unwrap()
1842            .segment_for_position(0)
1843            .unwrap();
1844        let (mut encryption, header) = start_encryption(&test_key(), b"", parameters).unwrap();
1845        let mut in_place = SegmentBuffer::new(parameters);
1846        in_place
1847            .prepare_plaintext(3)
1848            .unwrap()
1849            .copy_from_slice(b"abc");
1850        encryption
1851            .encrypt_segment_in_place(&mut in_place, segment)
1852            .unwrap();
1853
1854        let mut tampered = SegmentBuffer::new(parameters);
1855        tampered
1856            .prepare_ciphertext(SEGMENT_OVERHEAD + 3)
1857            .unwrap()
1858            .copy_from_slice(in_place.ciphertext().unwrap());
1859        *tampered
1860            .prepare_ciphertext(SEGMENT_OVERHEAD + 3)
1861            .unwrap()
1862            .last_mut()
1863            .unwrap() ^= 1;
1864
1865        // When the tampered buffer is decrypted in place
1866        // Then authentication fails
1867        let mut decryption = start_decryption(&test_key(), b"", parameters, &header).unwrap();
1868        assert_eq!(
1869            decryption.decrypt_segment_in_place(&mut tampered, segment),
1870            Err(Error::AuthenticationFailed)
1871        );
1872    }
1873
1874    #[test]
1875    fn segment_position_is_authenticated() {
1876        // Given a segment encrypted at position five
1877        let parameters = Parameters::SEGMENT_4_KIB;
1878        let (mut encryption, header) = start_encryption(&test_key(), b"", parameters).unwrap();
1879        let segment_bytes = encryption
1880            .encrypt_segment_at(b"data", 5, SegmentKind::Final)
1881            .unwrap();
1882
1883        // When it is decrypted at position six
1884        // Then authentication fails
1885        let mut decryption = start_decryption(&test_key(), b"", parameters, &header).unwrap();
1886        assert_eq!(
1887            decryption.decrypt_segment_at(&segment_bytes, 6, SegmentKind::Final),
1888            Err(Error::AuthenticationFailed)
1889        );
1890    }
1891
1892    #[test]
1893    fn final_indicator_is_authenticated() {
1894        // Given a final segment reframed with a non-final prefix and padded
1895        // to the full segment length
1896        let parameters = Parameters::SEGMENT_4_KIB;
1897        let (mut encryption, header) = start_encryption(&test_key(), b"", parameters).unwrap();
1898        let segment_bytes = encryption
1899            .encrypt_segment_at(b"data", 5, SegmentKind::Final)
1900            .unwrap();
1901        let mut forged_non_final = vec![0u8; parameters.ciphertext_segment_length()];
1902        forged_non_final[..4].copy_from_slice(&u32::MAX.to_be_bytes());
1903        forged_non_final[4..4 + segment_bytes.len() - 4].copy_from_slice(&segment_bytes[4..]);
1904
1905        // When it is decrypted as a non-final segment at its true position
1906        // Then authentication fails
1907        let mut decryption = start_decryption(&test_key(), b"", parameters, &header).unwrap();
1908        assert_eq!(
1909            decryption.decrypt_segment_at(&forged_non_final, 5, SegmentKind::NonFinal),
1910            Err(Error::AuthenticationFailed)
1911        );
1912    }
1913
1914    /// Encrypts `payload` as a raw in-place segment at position zero of a
1915    /// message with `trailing_plaintext` further bytes (non-final when more
1916    /// follows, final otherwise), returning the exactly sized encrypted
1917    /// storage, the segment layout, and a decryption state for the message.
1918    fn raw_encrypted_segment(
1919        payload: &[u8],
1920        trailing_plaintext: u64,
1921    ) -> (Vec<u8>, SegmentLayout, DecryptionState) {
1922        let parameters = Parameters::SEGMENT_4_KIB;
1923        let total = length_usize_to_u64(payload.len()) + trailing_plaintext;
1924        let segment = parameters
1925            .plaintext_layout(total)
1926            .unwrap()
1927            .segment_for_position(0)
1928            .unwrap();
1929        assert_eq!(segment.plaintext_length(), payload.len());
1930        let (mut encryption, header) = start_encryption(&test_key(), b"raw", parameters).unwrap();
1931        let mut storage = vec![0u8; segment.ciphertext_length()];
1932        storage[SEGMENT_PAYLOAD_OFFSET..SEGMENT_PAYLOAD_OFFSET + payload.len()]
1933            .copy_from_slice(payload);
1934        let written = encryption
1935            .encrypt_segment_in_place_raw(&mut storage, segment)
1936            .unwrap();
1937        assert_eq!(written, segment.ciphertext_length());
1938        let decryption = start_decryption(&test_key(), b"raw", parameters, &header).unwrap();
1939        (storage, segment, decryption)
1940    }
1941
1942    #[test]
1943    fn raw_in_place_final_segment_round_trips() {
1944        // Given a raw-encrypted final segment in exactly sized storage
1945        let (mut storage, segment, mut decryption) = raw_encrypted_segment(b"hello", 0);
1946
1947        // Then the final prefix encodes the exact segment length
1948        let prefix = u32::from_be_bytes(storage[..SEGMENT_PREFIX_LENGTH].try_into().unwrap());
1949        assert_eq!(prefix, u32::try_from(storage.len()).unwrap());
1950
1951        // When the same storage is decrypted in place
1952        // Then the original plaintext is recovered
1953        let plaintext = decryption
1954            .decrypt_segment_in_place_raw(&mut storage, segment)
1955            .unwrap();
1956        assert_eq!(&plaintext[..], b"hello");
1957    }
1958
1959    #[test]
1960    fn raw_in_place_non_final_segment_round_trips() {
1961        // Given a raw-encrypted full non-final segment
1962        let parameters = Parameters::SEGMENT_4_KIB;
1963        let full = vec![0x5a; parameters.plaintext_segment_length()];
1964        let (mut storage, segment, mut decryption) = raw_encrypted_segment(&full, 5);
1965
1966        // Then the storage spans one full segment with the non-final prefix
1967        assert_eq!(storage.len(), parameters.ciphertext_segment_length());
1968        let prefix = u32::from_be_bytes(storage[..SEGMENT_PREFIX_LENGTH].try_into().unwrap());
1969        assert_eq!(prefix, u32::MAX);
1970
1971        // When the same storage is decrypted in place
1972        // Then the original payload is recovered
1973        let plaintext = decryption
1974            .decrypt_segment_in_place_raw(&mut storage, segment)
1975            .unwrap();
1976        assert_eq!(&plaintext[..], &full[..]);
1977    }
1978
1979    #[test]
1980    fn raw_in_place_encryption_rejects_short_storage() {
1981        // Given storage one byte smaller than the encrypted segment needs
1982        let parameters = Parameters::SEGMENT_4_KIB;
1983        let segment = parameters.plaintext_layout(5).unwrap().final_segment();
1984        let required = segment.ciphertext_length();
1985        let (mut encryption, _header) = start_encryption(&test_key(), b"raw", parameters).unwrap();
1986        let mut storage = vec![0u8; required - 1];
1987
1988        // When the segment is encrypted, then the exact shortfall is reported
1989        assert_eq!(
1990            encryption.encrypt_segment_in_place_raw(&mut storage, segment),
1991            Err(Error::OutputTooSmall {
1992                actual: required - 1,
1993                required,
1994            })
1995        );
1996    }
1997
1998    #[test]
1999    fn raw_in_place_decryption_rejects_short_storage() {
2000        // Given storage one byte smaller than the segment layout requires
2001        let parameters = Parameters::SEGMENT_4_KIB;
2002        let segment = parameters.plaintext_layout(5).unwrap().final_segment();
2003        let required = segment.ciphertext_length();
2004        let (_, header) = start_encryption(&test_key(), b"raw", parameters).unwrap();
2005        let mut decryption = start_decryption(&test_key(), b"raw", parameters, &header).unwrap();
2006        let mut storage = vec![0u8; required - 1];
2007
2008        // When the segment is decrypted, then the requirement is reported
2009        assert!(matches!(
2010            decryption.decrypt_segment_in_place_raw(&mut storage, segment),
2011            Err(Error::InvalidCiphertextLength {
2012                actual,
2013                required: LengthRequirement::AtLeast(at_least),
2014            }) if actual == required - 1 && at_least == required
2015        ));
2016    }
2017
2018    #[test]
2019    fn raw_in_place_apis_reject_layout_from_another_parameter_set() {
2020        // Given a layout computed for a different parameter set
2021        let parameters = Parameters::SEGMENT_4_KIB;
2022        let foreign = Parameters::SEGMENT_1_MIB
2023            .plaintext_layout(5)
2024            .unwrap()
2025            .final_segment();
2026        let (mut encryption, header) = start_encryption(&test_key(), b"raw", parameters).unwrap();
2027        let mut decryption = start_decryption(&test_key(), b"raw", parameters, &header).unwrap();
2028        let mut storage = vec![0u8; foreign.ciphertext_length()];
2029
2030        // When either raw in-place entry point receives the foreign layout
2031        // Then it is rejected before any storage access
2032        assert_eq!(
2033            encryption.encrypt_segment_in_place_raw(&mut storage, foreign),
2034            Err(Error::InvalidParameters)
2035        );
2036        assert!(matches!(
2037            decryption.decrypt_segment_in_place_raw(&mut storage, foreign),
2038            Err(Error::InvalidParameters)
2039        ));
2040    }
2041
2042    #[test]
2043    fn raw_in_place_decryption_rejects_forged_final_prefix() {
2044        // Given a valid raw-encrypted final segment
2045        let (mut storage, segment, mut decryption) = raw_encrypted_segment(b"hello", 0);
2046
2047        // When the final length prefix disagrees with the actual length
2048        // Then the framing mismatch is rejected with both lengths
2049        let mut forged = storage.clone();
2050        let declared = u32::try_from(segment.ciphertext_length()).unwrap() + 1;
2051        forged[..SEGMENT_PREFIX_LENGTH].copy_from_slice(&declared.to_be_bytes());
2052        assert!(matches!(
2053            decryption.decrypt_segment_in_place_raw(&mut forged, segment),
2054            Err(Error::InvalidCiphertextLength {
2055                actual,
2056                required: LengthRequirement::Exactly(required),
2057            }) if actual == segment.ciphertext_length()
2058                && required == segment.ciphertext_length() + 1
2059        ));
2060
2061        // Then the untouched segment still decrypts afterward
2062        assert_eq!(
2063            &decryption
2064                .decrypt_segment_in_place_raw(&mut storage, segment)
2065                .unwrap()[..],
2066            b"hello"
2067        );
2068    }
2069
2070    #[test]
2071    fn raw_in_place_non_final_decryption_rejects_corrupt_prefix() {
2072        // Given a raw-encrypted non-final segment whose prefix is zeroed
2073        let full = vec![0x5a; Parameters::SEGMENT_4_KIB.plaintext_segment_length()];
2074        let (mut storage, segment, mut decryption) = raw_encrypted_segment(&full, 5);
2075        storage[..SEGMENT_PREFIX_LENGTH].fill(0);
2076
2077        // When the corrupted segment is decrypted in place
2078        // Then the non-final prefix is rejected as framing, not as content
2079        assert!(matches!(
2080            decryption.decrypt_segment_in_place_raw(&mut storage, segment),
2081            Err(Error::InvalidSegmentPrefix)
2082        ));
2083    }
2084
2085    #[test]
2086    fn in_place_wrappers_reject_buffers_from_another_parameter_set() {
2087        // Given a segment buffer built for a different parameter set
2088        let parameters = Parameters::SEGMENT_4_KIB;
2089        let segment = parameters.plaintext_layout(3).unwrap().final_segment();
2090        let (mut encryption, header) = start_encryption(&test_key(), b"raw", parameters).unwrap();
2091        let mut decryption = start_decryption(&test_key(), b"raw", parameters, &header).unwrap();
2092        let mut foreign = SegmentBuffer::new(Parameters::SEGMENT_64_B);
2093
2094        // When the foreign buffer holds plaintext for encryption
2095        // Then the parameter mismatch is rejected
2096        foreign
2097            .prepare_plaintext(3)
2098            .unwrap()
2099            .copy_from_slice(b"abc");
2100        assert_eq!(
2101            encryption.encrypt_segment_in_place(&mut foreign, segment),
2102            Err(Error::InvalidParameters)
2103        );
2104
2105        // When the foreign buffer holds ciphertext for decryption
2106        // Then the parameter mismatch is rejected the same way
2107        foreign.prepare_ciphertext(SEGMENT_OVERHEAD + 3).unwrap();
2108        assert!(matches!(
2109            decryption.decrypt_segment_in_place(&mut foreign, segment),
2110            Err(Error::InvalidParameters)
2111        ));
2112    }
2113
2114    #[test]
2115    fn in_place_decryption_rejects_prepared_length_disagreeing_with_layout() {
2116        // Given prepared ciphertext one byte longer than the layout declares
2117        let parameters = Parameters::SEGMENT_4_KIB;
2118        let segment = parameters.plaintext_layout(3).unwrap().final_segment();
2119        let (_, header) = start_encryption(&test_key(), b"raw", parameters).unwrap();
2120        let mut decryption = start_decryption(&test_key(), b"raw", parameters, &header).unwrap();
2121        let mut buffer = SegmentBuffer::new(parameters);
2122        buffer.prepare_ciphertext(SEGMENT_OVERHEAD + 4).unwrap();
2123
2124        // When the buffer is decrypted against the shorter layout
2125        // Then the exact length disagreement is reported
2126        assert!(matches!(
2127            decryption.decrypt_segment_in_place(&mut buffer, segment),
2128            Err(Error::InvalidCiphertextLength {
2129                actual,
2130                required: LengthRequirement::Exactly(required),
2131            }) if actual == SEGMENT_OVERHEAD + 4 && required == SEGMENT_OVERHEAD + 3
2132        ));
2133    }
2134}