Permalink
Browse files

Introduce TransportError type (#374)

* Introduce TransportError type

It is not very convenient to use () for Result error type. Unit type
does not implement Error trait and thus cannot be used with the "?"
operator. It is not very convenient for users which may need to return
arbitrary errors from their transport callbacks. These may be IO errors
from the standard library or some custom errors.

Introduce a new type "TransportError" which will represent errors for
transport callbacks. It is an enumeration which can hold three kinds
of errors:

  - Arbitrary std::error::Error type

    This kind allows to wrap any other error into a TransportError.
    We implement a From conversion so that TransportError works with
    the "?" operator. This makes it very convenient to forward existing
    errors out of SecureSessionTransport callbacks.

  - Custom human-readable string

    This kind is useful when you don't have an Error handy but still
    want to return something more descriptive. Anything that can be
    converted into a String will do.

  - Unspecified kind

    This kind is useful to explicity say that we do not have any
    detailed information on the error. It will be used in some
    conversions of raw Themis errors.

Unfortunately, it's not possible to implement Error trait for
TransportError because of a blanket impl on From/Into traits:
it conflicts with out custom From<T: Error> implementation.
Well, it's not a big deal for now, but may bite us later.

However, we do implement all other Error requirements like Display
so the error should be convenient enough to use.

Note that the error itself is a struct which wraps an enumeration.
This allows us to not expose enumeration variants to the user.

Also note the additional "Send + Sync" trait bounds on the Error impl.
It is important to have these traits implemented if we want to be able
to transfer errors between threads.

* Use TransportError in Secure Session

Update the SecureSessionTransport trait to use the new type.

Update usage in tests. Note how "?" is used to forward channel errors
and TransportError::new() usage for custom error reporting.
  • Loading branch information...
ilammy committed Feb 11, 2019
1 parent 37cb28a commit 7b19328d1fb862f9d9ca0744844e7953f92d768b
Showing with 132 additions and 15 deletions.
  1. +121 −6 src/wrappers/themis/rust/src/secure_session.rs
  2. +11 −9 tests/rust/secure_session.rs
@@ -17,6 +17,8 @@
//! **Secure Session** is a lightweight mechanism for securing any kind of network communication
//! (both private and public networks, including the Internet).

use std::error;
use std::fmt;
use std::os::raw::{c_int, c_void};
use std::{ptr, result, slice};

@@ -59,8 +61,6 @@ struct SecureSessionContext {
/// [`get_public_key_for_id`]: trait.SecureSessionTransport.html#tymethod.get_public_key_for_id
#[allow(unused_variables)]
pub trait SecureSessionTransport {
// TODO: consider send/receive use std::io::Error for errors (or a custom type)

/// Send the provided data to the peer, return the number of bytes transferred.
///
/// This callback will be called when Secure Session needs to send some data to its peer.
@@ -73,8 +73,8 @@ pub trait SecureSessionTransport {
/// [`connect`]: struct.SecureSession.html#method.connect
/// [`negotiate_transport`]: struct.SecureSession.html#method.negotiate_transport
/// [`send`]: struct.SecureSession.html#method.send
fn send_data(&mut self, data: &[u8]) -> result::Result<usize, ()> {
Err(())
fn send_data(&mut self, data: &[u8]) -> result::Result<usize, TransportError> {
Err(TransportError::unspecified())
}

/// Receive some data from the peer into the provided buffer, return the number of bytes.
@@ -88,8 +88,8 @@ pub trait SecureSessionTransport {
///
/// [`negotiate_transport`]: struct.SecureSession.html#method.negotiate_transport
/// [`receive`]: struct.SecureSession.html#method.receive
fn receive_data(&mut self, data: &mut [u8]) -> result::Result<usize, ()> {
Err(())
fn receive_data(&mut self, data: &mut [u8]) -> result::Result<usize, TransportError> {
Err(TransportError::unspecified())
}

/// Notification about connection state of Secure Session.
@@ -103,6 +103,121 @@ pub trait SecureSessionTransport {
fn get_public_key_for_id(&mut self, id: &[u8]) -> Option<EcdsaPublicKey>;
}

/// Transport layer error.
///
/// This is a type representing failure in transport layer of [`SecureSessionTransport`],
/// namely its [`send_data`] and [`receive_data`] methods.
///
/// [`SecureSessionTransport`]: trait.SecureSessionTransport.html
/// [`send_data`]: trait.SecureSessionTransport.html#method.send_data
/// [`receive_data`]: trait.SecureSessionTransport.html#method.receive_data
///
/// # Examples
///
/// `TransportError` can conveniently wrap any other error using `?` operator.
/// You can also explicitly construct an error with a descriptive string.
///
/// ```no_run
/// use std::io::{Read, Write};
/// use std::net::TcpStream;
///
/// use themis::secure_session::{SecureSessionTransport, TransportError};
/// # use themis::keys::EcdsaPublicKey;
///
/// struct SocketTransport {
/// socket: TcpStream,
/// }
///
/// impl SecureSessionTransport for SocketTransport {
/// fn send_data(&mut self, data: &[u8]) -> Result<usize, TransportError> {
/// if data.len() >= 256 {
/// return Err(TransportError::new(format!("too long data: {} bytes", data.len())));
/// }
///
/// let len_buffer = [data.len() as u8];
/// self.socket.write_all(&len_buffer)?;
///
/// self.socket.write_all(data)?;
/// Ok(data.len())
/// }
///
/// fn receive_data(&mut self, data: &mut [u8]) -> Result<usize, TransportError> {
/// let mut len_buffer = [0];
/// self.socket.read_exact(&mut len_buffer)?;
///
/// let len = len_buffer[0] as usize;
/// if data.len() < len {
/// return Err(TransportError::new("buffer too short"));
/// }
///
/// self.socket.read_exact(&mut data[0..len])?;
/// Ok(len)
/// }
///
/// // Other methods omitted
/// #
/// # fn get_public_key_for_id(&mut self, id: &[u8]) -> Option<EcdsaPublicKey> {
/// # None
/// # }
/// }
/// ```
pub struct TransportError {
inner: TransportErrorInner,
}

enum TransportErrorInner {
Unspecified,
Simple(String),
Custom(Box<dyn error::Error + Send + Sync>),
}

impl fmt::Display for TransportError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.inner {
TransportErrorInner::Unspecified => write!(f, "Secure Session transport failed"),
TransportErrorInner::Simple(s) => write!(f, "Secure Session transport failed: {}", s),
TransportErrorInner::Custom(e) => write!(f, "Secure Session transport failed: {}", e),
}
}
}

impl fmt::Debug for TransportError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.inner {
TransportErrorInner::Unspecified => write!(f, "TransportError::Unspecified"),
TransportErrorInner::Simple(s) => write!(f, "TransportError::Simple({:?})", s),
TransportErrorInner::Custom(e) => write!(f, "TransportError::Custom({:?})", e),
}
}
}

impl<T> From<T> for TransportError
where
T: error::Error + Send + Sync + 'static,
{
fn from(error: T) -> Self {
TransportError {
inner: TransportErrorInner::Custom(Box::new(error)),
}
}
}

impl TransportError {
/// Returns a new error with a human-readable description.
pub fn new(description: impl Into<String>) -> TransportError {
TransportError {
inner: TransportErrorInner::Simple(description.into()),
}
}

/// Returns an unspecified error.
pub fn unspecified() -> TransportError {
TransportError {
inner: TransportErrorInner::Unspecified,
}
}
}

/// State of Secure Session connection.
#[derive(PartialEq, Eq)]
pub enum SecureSessionState {
@@ -18,7 +18,7 @@ use std::sync::mpsc::{channel, Receiver, Sender};

use themis::keygen::gen_ec_key_pair;
use themis::keys::EcdsaPublicKey;
use themis::secure_session::{SecureSession, SecureSessionTransport};
use themis::secure_session::{SecureSession, SecureSessionTransport, TransportError};

struct DummyTransport {
key_map: Rc<BTreeMap<Vec<u8>, EcdsaPublicKey>>,
@@ -66,17 +66,19 @@ impl ChannelTransport {
}

impl SecureSessionTransport for ChannelTransport {
fn send_data(&mut self, data: &[u8]) -> Result<usize, ()> {
self.tx
.send(data.to_vec())
.map(|_| data.len())
.map_err(|_| ())
fn send_data(&mut self, data: &[u8]) -> Result<usize, TransportError> {
self.tx.send(data.to_vec())?;
Ok(data.len())
}

fn receive_data(&mut self, data: &mut [u8]) -> Result<usize, ()> {
let msg = self.rx.recv().map_err(|_| ())?;
fn receive_data(&mut self, data: &mut [u8]) -> Result<usize, TransportError> {
let msg = self.rx.recv()?;
if msg.len() > data.len() {
return Err(());
return Err(TransportError::new(format!(
"buffer too small: {} bytes, need {} bytes",
data.len(),
msg.len(),
)));
}
data[0..msg.len()].copy_from_slice(&msg);
Ok(msg.len())

0 comments on commit 7b19328

Please sign in to comment.