Skip to content

Commit f74b8e6

Browse files
authored
Merge pull request #1871 from haroldbruintjes/feature/cipher-inplace
Add in-place cipher update method
2 parents 05ce5bc + edf3a16 commit f74b8e6

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

openssl/src/cipher_ctx.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,50 @@ impl CipherCtxRef {
591591
Ok(len)
592592
}
593593

594+
/// Like [`Self::cipher_update`] except that it writes output into the
595+
/// `data` buffer. The `inlen` parameter specifies the number of bytes in
596+
/// `data` that are considered the input. For streaming ciphers, the size of
597+
/// `data` must be at least the input size. Otherwise, it must be at least
598+
/// an additional block size larger.
599+
///
600+
/// Note: Use [`Self::cipher_update`] with no output argument to write AAD.
601+
///
602+
/// # Panics
603+
///
604+
/// This function panics if the input size cannot be represented as `int` or
605+
/// exceeds the buffer size, or if the output buffer does not contain enough
606+
/// additional space.
607+
#[corresponds(EVP_CipherUpdate)]
608+
pub fn cipher_update_inplace(
609+
&mut self,
610+
data: &mut [u8],
611+
inlen: usize,
612+
) -> Result<usize, ErrorStack> {
613+
assert!(inlen <= data.len(), "Input size may not exceed buffer size");
614+
let block_size = self.block_size();
615+
if block_size != 1 {
616+
assert!(
617+
data.len() >= inlen + block_size,
618+
"Output buffer size must be at least {} bytes.",
619+
inlen + block_size
620+
);
621+
}
622+
623+
let inlen = c_int::try_from(inlen).unwrap();
624+
let mut outlen = 0;
625+
unsafe {
626+
cvt(ffi::EVP_CipherUpdate(
627+
self.as_ptr(),
628+
data.as_mut_ptr(),
629+
&mut outlen,
630+
data.as_ptr(),
631+
inlen,
632+
))
633+
}?;
634+
635+
Ok(outlen as usize)
636+
}
637+
594638
/// Finalizes the encryption or decryption process.
595639
///
596640
/// Any remaining data will be written to the output buffer.
@@ -778,6 +822,26 @@ mod test {
778822

779823
ctx.cipher_final_vec(&mut vec![0; 0]).unwrap();
780824

825+
// encrypt again, but use in-place encryption this time
826+
// First reset the IV
827+
ctx.encrypt_init(None, None, Some(&iv)).unwrap();
828+
ctx.set_padding(false);
829+
let mut data_inplace: [u8; 32] = [1; 32];
830+
let outlen = ctx
831+
.cipher_update_inplace(&mut data_inplace[0..15], 15)
832+
.unwrap();
833+
assert_eq!(15, outlen);
834+
835+
let outlen = ctx
836+
.cipher_update_inplace(&mut data_inplace[15..32], 17)
837+
.unwrap();
838+
assert_eq!(17, outlen);
839+
840+
ctx.cipher_final(&mut [0u8; 0]).unwrap();
841+
842+
// Check that the resulting data is encrypted in the same manner
843+
assert_eq!(data_inplace.as_slice(), output.as_slice());
844+
781845
// try to decrypt
782846
ctx.decrypt_init(Some(cipher), Some(&key), Some(&iv))
783847
.unwrap();
@@ -800,6 +864,19 @@ mod test {
800864
ctx.cipher_final_vec(&mut vec![0; 0]).unwrap();
801865
// check if the decrypted blocks are the same as input (all ones)
802866
assert_eq!(output_decrypted, vec![1; 32]);
867+
868+
// decrypt again, but now the output in-place
869+
ctx.decrypt_init(None, None, Some(&iv)).unwrap();
870+
ctx.set_padding(false);
871+
872+
let outlen = ctx.cipher_update_inplace(&mut output[0..15], 15).unwrap();
873+
assert_eq!(15, outlen);
874+
875+
let outlen = ctx.cipher_update_inplace(&mut output[15..], 17).unwrap();
876+
assert_eq!(17, outlen);
877+
878+
ctx.cipher_final_vec(&mut vec![0; 0]).unwrap();
879+
assert_eq!(output_decrypted, output);
803880
}
804881

805882
#[test]

0 commit comments

Comments
 (0)