secio: keep ciphertext if start_send is not ready (#494)

* secio: keep ciphertext if start_send is not ready

Otherwise ciphertext may be thrown away and once sent, the cipher states
have diverged, resulting in nonsense messages on decryption side.

* Also attempt to send pending data `Sink::close`.

* Inline `send_pending`.
This commit is contained in:
Toralf Wittner 2018-09-18 22:51:13 +02:00 committed by GitHub
parent 8cf17f49e1
commit 7fb09fbf20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -22,10 +22,7 @@
use bytes::BytesMut; use bytes::BytesMut;
use super::StreamCipher; use super::StreamCipher;
use futures::sink::Sink; use futures::prelude::*;
use futures::stream::Stream;
use futures::Poll;
use futures::StartSend;
use ring::hmac; use ring::hmac;
/// Wraps around a `Sink`. Encodes the buffers passed to it and passes it to the underlying sink. /// Wraps around a `Sink`. Encodes the buffers passed to it and passes it to the underlying sink.
@ -39,18 +36,16 @@ pub struct EncoderMiddleware<S> {
cipher_state: StreamCipher, cipher_state: StreamCipher,
hmac_key: hmac::SigningKey, hmac_key: hmac::SigningKey,
raw_sink: S, raw_sink: S,
pending: Option<BytesMut> // buffer encrypted data which can not be sent right away
} }
impl<S> EncoderMiddleware<S> { impl<S> EncoderMiddleware<S> {
pub fn new( pub fn new(raw: S, cipher: StreamCipher, key: hmac::SigningKey) -> EncoderMiddleware<S> {
raw_sink: S,
cipher: StreamCipher,
hmac_key: hmac::SigningKey,
) -> EncoderMiddleware<S> {
EncoderMiddleware { EncoderMiddleware {
cipher_state: cipher, cipher_state: cipher,
hmac_key, hmac_key: key,
raw_sink, raw_sink: raw,
pending: None
} }
} }
} }
@ -63,22 +58,42 @@ where
type SinkError = S::SinkError; type SinkError = S::SinkError;
fn start_send(&mut self, mut data_buf: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(&mut self, mut data_buf: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
// TODO if SinkError gets refactor to SecioError, if let Some(data) = self.pending.take() {
// then use try_apply_keystream if let AsyncSink::NotReady(data) = self.raw_sink.start_send(data)? {
self.pending = Some(data);
return Ok(AsyncSink::NotReady(data_buf))
}
}
debug_assert!(self.pending.is_none());
// TODO if SinkError gets refactor to SecioError, then use try_apply_keystream
self.cipher_state.apply_keystream(&mut data_buf[..]); self.cipher_state.apply_keystream(&mut data_buf[..]);
let signature = hmac::sign(&self.hmac_key, &data_buf[..]); let signature = hmac::sign(&self.hmac_key, &data_buf[..]);
data_buf.extend_from_slice(signature.as_ref()); data_buf.extend_from_slice(signature.as_ref());
self.raw_sink.start_send(data_buf) if let AsyncSink::NotReady(data) = self.raw_sink.start_send(data_buf)? {
self.pending = Some(data)
}
Ok(AsyncSink::Ready)
} }
#[inline] #[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
if let Some(data) = self.pending.take() {
if let AsyncSink::NotReady(data) = self.raw_sink.start_send(data)? {
self.pending = Some(data);
return Ok(Async::NotReady)
}
}
self.raw_sink.poll_complete() self.raw_sink.poll_complete()
} }
#[inline] #[inline]
fn close(&mut self) -> Poll<(), Self::SinkError> { fn close(&mut self) -> Poll<(), Self::SinkError> {
if let Some(data) = self.pending.take() {
if let AsyncSink::NotReady(data) = self.raw_sink.start_send(data)? {
self.pending = Some(data);
return Ok(Async::NotReady)
}
}
self.raw_sink.close() self.raw_sink.close()
} }
} }