aescrypt_rs/decryptor/stream/
versions.rs1use crate::aliases::{Aes256Key, Iv16};
6use crate::decryptor::stream::context::DecryptionContext;
7use crate::decryptor::stream::utils::{
8 extract_hmac_scattered, extract_hmac_simple, write_final_modulo, write_final_pkcs7,
9};
10use crate::error::AescryptError;
11use aes::cipher::KeyInit;
12use aes::Aes256Dec;
13use hmac::{Hmac, Mac};
14use sha2::Sha256;
15use std::io::{Read, Write};
16
17type HmacSha256 = Hmac<Sha256>;
18
19#[derive(Clone, Copy)]
20pub enum StreamConfig {
21 V0 { reserved_modulo: u8 },
22 V1,
23 V2,
24 V3,
25}
26
27#[inline(always)]
28pub fn decrypt_ciphertext_stream<R, W>(
29 mut input_reader: R,
30 mut output_writer: W,
31 initial_vector: &Iv16,
32 encryption_key: &Aes256Key,
33 config: StreamConfig,
34) -> Result<(), AescryptError>
35where
36 R: Read,
37 W: Write,
38{
39 let key_bytes = encryption_key.expose_secret();
40 let cipher = Aes256Dec::new(key_bytes.into());
41
42 let mut hmac = <HmacSha256 as Mac>::new_from_slice(key_bytes)
44 .expect("encryption_key is always 32 bytes — valid HMAC key");
45
46 let mut ctx = DecryptionContext::new_with_iv(initial_vector);
47 ctx.decrypt_cbc_loop(&mut input_reader, &mut output_writer, &cipher, &mut hmac)?;
48
49 ctx.advance_tail();
50 let remaining = ctx.remaining();
51
52 match config {
53 StreamConfig::V0 { reserved_modulo } => {
54 if remaining != 32 {
55 return Err(AescryptError::Header(
56 "v0: expected 32-byte HMAC trailer".into(),
57 ));
58 }
59
60 let expected_hmac = extract_hmac_simple(&ctx);
61
62 if hmac.finalize().into_bytes().as_slice() != expected_hmac {
63 return Err(AescryptError::Header("HMAC verification failed".into()));
64 }
65
66 write_final_modulo(&ctx, &mut output_writer, reserved_modulo)?;
67 }
68
69 StreamConfig::V1 | StreamConfig::V2 => {
70 if remaining != 33 {
71 return Err(AescryptError::Header(
72 "v1/v2: expected 33-byte trailer".into(),
73 ));
74 }
75
76 let (expected_hmac, modulo_byte) = extract_hmac_scattered(&ctx);
77
78 if hmac.finalize().into_bytes().as_slice() != expected_hmac {
79 return Err(AescryptError::Header("HMAC verification failed".into()));
80 }
81
82 write_final_modulo(&ctx, &mut output_writer, modulo_byte)?;
83 }
84
85 StreamConfig::V3 => {
86 if remaining != 32 {
87 return Err(AescryptError::Header(
88 "v3: expected 32-byte HMAC trailer".into(),
89 ));
90 }
91
92 let expected_hmac = extract_hmac_simple(&ctx);
93
94 if hmac.finalize().into_bytes().as_slice() != expected_hmac {
95 return Err(AescryptError::Header("HMAC verification failed".into()));
96 }
97
98 write_final_pkcs7(&ctx, &mut output_writer)?;
99 }
100 }
101
102 Ok(())
103}