Skip to content

Commit 57aea51

Browse files
authored
cipher: add methods for writing keystream (#1907)
1 parent 946e0f4 commit 57aea51

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

cipher/src/stream.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ pub trait StreamCipher {
104104
self.try_apply_keystream_inout(buf.into())
105105
}
106106

107+
/// Write keystream to `buf`.
108+
///
109+
/// If end of the keystream will be achieved with the given data length,
110+
/// method will return [`StreamCipherError`] without modifying provided `data`.
111+
#[inline]
112+
fn try_write_keystream(&mut self, buf: &mut [u8]) -> Result<(), StreamCipherError> {
113+
buf.fill(0);
114+
self.try_apply_keystream(buf)
115+
}
116+
107117
/// Apply keystream to `inout` data.
108118
///
109119
/// It will XOR generated keystream with the data behind `in` pointer
@@ -130,6 +140,16 @@ pub trait StreamCipher {
130140
self.try_apply_keystream(buf).unwrap();
131141
}
132142

143+
/// Write keystream to `buf`.
144+
///
145+
/// # Panics
146+
/// If end of the keystream will be reached with the given data length,
147+
/// method will panic without modifying the provided `data`.
148+
#[inline]
149+
fn write_keystream(&mut self, buf: &mut [u8]) {
150+
self.try_write_keystream(buf).unwrap();
151+
}
152+
133153
/// Apply keystream to data buffer-to-buffer.
134154
///
135155
/// It will XOR generated keystream with data from the `input` buffer

cipher/src/stream/wrapper.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use super::{
33
StreamCipherSeekCore, errors::StreamCipherError,
44
};
55
use core::fmt;
6-
use crypto_common::{Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser, typenum::Unsigned};
6+
use crypto_common::{
7+
Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser, array::Array, typenum::Unsigned,
8+
};
79
use inout::InOutBuf;
810
#[cfg(feature = "zeroize")]
911
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -170,6 +172,58 @@ impl<T: StreamCipherCore> StreamCipher for StreamCipherCoreWrapper<T> {
170172

171173
Ok(())
172174
}
175+
176+
#[inline]
177+
fn try_write_keystream(&mut self, mut data: &mut [u8]) -> Result<(), StreamCipherError> {
178+
self.check_remaining(data.len())?;
179+
180+
let pos = usize::from(self.get_pos());
181+
let rem = usize::from(self.remaining());
182+
let data_len = data.len();
183+
184+
if rem != 0 {
185+
if data_len <= rem {
186+
data.copy_from_slice(&self.buffer[pos..][..data_len]);
187+
// SAFETY: we have checked that `data_len` is less or equal to length
188+
// of remaining keystream data, thus `pos + data_len` can not be bigger
189+
// than block size. Since `pos` is never zero, `pos + data_len` can not
190+
// be zero. Thus `pos + data_len` satisfies the safety invariant required
191+
// by `set_pos_unchecked`.
192+
unsafe {
193+
self.set_pos_unchecked(pos + data_len);
194+
}
195+
return Ok(());
196+
}
197+
let (left, right) = data.split_at_mut(rem);
198+
data = right;
199+
left.copy_from_slice(&self.buffer[pos..]);
200+
}
201+
202+
let (blocks, tail) = Array::slice_as_chunks_mut(data);
203+
self.core.write_keystream_blocks(blocks);
204+
205+
let new_pos = if tail.is_empty() {
206+
T::BlockSize::USIZE
207+
} else {
208+
// Note that we temporarily write a pseudo-random byte into
209+
// the first byte of `self.buffer`. It may break the safety invariant,
210+
// but after writing keystream block with `tail`, we immediately
211+
// overwrite the first byte with a correct value.
212+
self.core.write_keystream_block(&mut self.buffer);
213+
tail.copy_from_slice(&self.buffer[..tail.len()]);
214+
tail.len()
215+
};
216+
217+
// SAFETY: `into_chunks` always returns tail with size
218+
// less than block size. If `tail.len()` is zero, we replace
219+
// it with block size. Thus the invariant required by
220+
// `set_pos_unchecked` is satisfied.
221+
unsafe {
222+
self.set_pos_unchecked(new_pos);
223+
}
224+
225+
Ok(())
226+
}
173227
}
174228

175229
impl<T: StreamCipherSeekCore> StreamCipherSeek for StreamCipherCoreWrapper<T> {

0 commit comments

Comments
 (0)