Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ target
Cargo.lock
*.bk
build.sh

.DS_Store/
17 changes: 9 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
[package]
name = "caesar"
description = "A drop-in replacement for the Rust standard library TCP listener with TLSv1.2 enabled."
documentation = "https://arturovm.me/rustdoc/caesar/index.html"
repository = "https://github.com/Postage/caesar"
name = "foxtls"
description = "A lightweight non-blocking TLS wrapper for the Rust standard library TCP listener."
documentation = "https://fox4c.github.io/FoxTLS/"
repository = "https://github.com/Fox4c/FoxTLS"
readme = "README.md"
keywords = ["tls", "ssl", "tcp", "crypto"]
keywords = ["tls", "ssl", "tcp", "crypto", "mio", "non-blocking"]
license = "MIT"

version = "0.2.0"
authors = ["Arturo Vergara <hello@arturovm.me>"]
version = "0.0.1"
authors = ["Joshua Steffensky <steffensky@fox4c.com>"]

[dependencies]
openssl = "0.7.6"
openssl = "^0.7.6"
mio = "^0.5.1"

[features]
default = ["tlsv1_2", "rfc5114"]
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Arturo Vergara <hello@arturovm.me>
Copyright (c) 2015 Fox4c GbR <info@fox4c.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
39 changes: 9 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,28 @@
# Caesar
# FoxTLS

A drop-in replacement for the Rust standard library TCP listener with TLSv1.2 enabled.
FoxTLS is a lightweight non-blocking TLS wrapper for the Rust standard library TCP listener.
It is based on the [Postage/caesar](https://github.com/Postage/caesar).

- [Documentation](https://arturovm.me/rustdoc/caesar/index.html)
- [Crate](https://crates.io/crates/caesar)
- [Documentation](https://fox4c.github.io/FoxTLS/)
- [Crate](https://crates.io/crates/FoxTLS)

_Note: This library hasn't been tested._

## Introduction

This library abstracts over a regular TCP listener from the Rust standard library, and provides a drop-in* interface replacement that layers TLSv1.2 with a set of strong cipher suites on top of the connection.
This library abstracts over a regular TCP listener from the Rust standard library, and provides a drop-in* interface replacement that layers TLS with a set of strong cipher suites on top of the connection.

It uses the [OpenSSL library Rust bindings](https://github.com/sfackler/rust-openssl) by Steven Fackler for the underlying TLS functionality. If you don't trust OpenSSL (I don't), you can compile this crate against LibreSSL (instructions provided below).
It uses the [OpenSSL library Rust bindings](https://github.com/sfackler/rust-openssl) by Steven Fackler for the underlying TLS functionality.

_* It's only necessary to prepend `Tls` to the regular types (e.g. `TlsTcpListener`), and write error handling code for the new error types._

## Compiling

### Prerequisites

- OpenSSL/LibreSSL headers

How and where you install these headers depends on your platform. A quick Google search should do the trick.

### Building

As described in the [README](https://github.com/sfackler/rust-openssl) for the Rust OpenSSL bindings, there are various ways of compiling this crate, depending on your platform. However, the most universal route, is to configure your build manually with environment variables (and this is required anyway for compiling against LibreSSL). There are only three:

- `OPENSSL_LIB_DIR`: Use this to specify the path to the _lib_ dir of your SSL library of choice
- `OPENSSL_INCLUDE_DIR`: Use this to specify the path to the _include_ dir of your SSL library of choice
- `OPENSSL_STATIC`: [optional] This is a boolean variable specifying whether to statically link against your SSL library

The complete command might look something like this:

```bash
$ env OPENSSL_LIB_DIR="/usr/local/opt/libressl/lib" \
OPENSSL_INCLUDE_DIR="/usr/local/opt/libressl/include" \
OPENSSL_STATIC=true cargo build
```
## Usage

Caesar provides a `CaesarError` enum type, with a variant for I/O errors and another one for SSL errors. You can find the specifics in the library [docs](https://arturovm.me/rustdoc/caesar/index.html).
FoxTLS provides a `FoxTLSError` enum type, with a variant for I/O errors and another one for SSL errors. You can find the specifics in the library [docs](https://fox4c.github.io/FoxTLS/FoxTLS/index.html).

```rust
extern crate caesar;
extern crate FoxTlS;

use caesar::{TlsTcpListener, TlsTcpStream};
use std::thread;
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@
//! drop(listener);
//! ```

extern crate mio;
extern crate openssl;

mod types;
mod tcp;

pub use types::{Result, CaesarError};
pub use types::{Result, FoxTLSError};
pub use tcp::*;
90 changes: 67 additions & 23 deletions src/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::cell::RefCell;
use std::io::{self, Read, Write};
use std::net::{SocketAddr, ToSocketAddrs, TcpListener, TcpStream, Shutdown};
use std::time::Duration;
use std::net::{SocketAddr, ToSocketAddrs, Shutdown};
use mio::tcp::{TcpListener, TcpStream};
//use std::time::Duration;

use openssl::ssl::{self, SslContext, SslMethod, Ssl, SslStream};
use openssl::ssl::{self, SslContext, SslMethod, Ssl, SslStream, SslContextOptions};
use openssl::dh::DH;
use openssl::x509::X509FileType;

use super::types::Result;

/// Abstracts over a `TcpListener`, and layers TLSv1.2 on top.
/// Abstracts over a `TcpListener`, and layers TLS on top.
#[derive(Debug)]
pub struct TlsTcpListener {
listener: TcpListener,
Expand All @@ -32,23 +33,33 @@ impl TlsTcpListener {
/// Behaves exactly like `TcpStream::bind`. It expects paths to key and certificate files in
/// PEM format.
pub fn bind<A: ToSocketAddrs>(addr: A, key: &str, cert: &str) -> Result<TlsTcpListener> {
TlsTcpListener::bind_expert(addr, key, cert, None, None, None)
}

//new expert bind methode => gives additional options to define sslmethode, ssloptions, and list of ciphers to use
pub fn bind_expert<A: ToSocketAddrs>(addr: A, key: &str, cert: &str, method: Option<SslMethod>, opts: Option<SslContextOptions>, cipher_list: Option<String>) -> Result<TlsTcpListener> {
let sockaddr = try!(addr.to_socket_addrs()).next().unwrap();
// create listener
let listener = try!(TcpListener::bind(addr));
let ctx = try!(new_ssl_context(&key, &cert));
let listener = try!(TcpListener::bind(&sockaddr));
let ctx = try!(new_ssl_context(&key, &cert, method, opts, cipher_list));
Ok(TlsTcpListener {
listener: listener,
ctx: ctx,
})
}

pub fn accept(&self) -> Result<(TlsTcpStream, SocketAddr)> {
pub fn accept(&self) -> Result<Option<(TlsTcpStream, SocketAddr)>> {
// acept from bare TCP stream
let (stream, addr) = try!(self.listener.accept());
let acceptoption = try!(self.listener.accept());
let (stream, addr) = match acceptoption{
Some((s,a)) => (s,a),
None => return Ok(None),
};
// create SSL object with stored context
let ssl = try!(Ssl::new(&self.ctx));
// accept from encrypted stream
let tls_stream = try!(SslStream::accept(ssl, stream));
Ok((TlsTcpStream(RefCell::new(tls_stream)), addr))
Ok(Some((TlsTcpStream(RefCell::new(tls_stream)), addr)))
}

pub fn incoming(&self) -> Incoming {
Expand All @@ -59,7 +70,24 @@ impl TlsTcpListener {
impl<'a> Iterator for Incoming<'a> {
type Item = Result<TlsTcpStream>;
fn next(&mut self) -> Option<Result<TlsTcpStream>> {
Some(self.listener.accept().map(|p| p.0))
let mut tmp: Option<(TlsTcpStream, SocketAddr)> = match self.listener.accept() {
Err(x) => return Some(Err(x)),
Ok(x) => x,
};

loop{
match tmp{
None => tmp = match self.listener.accept(){
Err(x) => return Some(Err(x)),
Ok(x) => x,
},
Some(_) => break,
}
}

let (stream, _) = tmp.unwrap();

Some(Ok(stream))
}
}

Expand All @@ -75,10 +103,7 @@ impl TlsTcpStream {
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.0.borrow().get_ref().shutdown(how)
}

pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.0.borrow().get_ref().set_read_timeout(dur)
}

}

impl Read for TlsTcpStream {
Expand Down Expand Up @@ -113,15 +138,31 @@ impl<'a> Write for &'a TlsTcpStream {
}
}

fn new_ssl_context(key: &str, cert: &str) -> Result<SslContext> {
let mut ctx = try!(ssl::SslContext::new(SslMethod::Tlsv1_2));
// use recommended settings
let opts = ssl::SSL_OP_CIPHER_SERVER_PREFERENCE | ssl::SSL_OP_NO_COMPRESSION |
ssl::SSL_OP_NO_TICKET | ssl::SSL_OP_NO_SSLV2 |
ssl::SSL_OP_NO_SSLV3 | ssl::SSL_OP_NO_TLSV1 | ssl::SSL_OP_NO_TLSV1_1;
fn new_ssl_context(key: &str, cert: &str, method: Option<SslMethod>, opts: Option<SslContextOptions>, cipher_list: Option<String>) -> Result<SslContext> {
let mut ctx = try!(ssl::SslContext::new(
match method{
Some(x) => x,
None => SslMethod::Tlsv1_2, //default option
}));


let opts = match opts{
Some(x) => x,

// use recommended settings => default
None => ssl::SSL_OP_CIPHER_SERVER_PREFERENCE | ssl::SSL_OP_NO_COMPRESSION |
ssl::SSL_OP_NO_TICKET | ssl::SSL_OP_NO_SSLV2 |
ssl::SSL_OP_NO_SSLV3 | ssl::SSL_OP_NO_TLSV1 | ssl::SSL_OP_NO_TLSV1_1,
};

ctx.set_options(opts);
// set strong cipher suites
try!(ctx.set_cipher_list("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\


let ciphers = match cipher_list {
Some(x) => x,

// set strong cipher suites
None => String::from("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:\
Expand All @@ -133,7 +174,10 @@ fn new_ssl_context(key: &str, cert: &str) -> Result<SslContext> {
ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\
EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:\
AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:\
!DSS"));
!DSS"),
};

try!(ctx.set_cipher_list(ciphers.as_str()));
// enable forward secrecy
let dh = try!(DH::get_2048_256());
try!(ctx.set_tmp_dh(dh));
Expand Down
32 changes: 16 additions & 16 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,46 @@ use std::convert::From;
use openssl::ssl::error::SslError;

#[derive(Debug)]
pub enum CaesarError {
pub enum FoxTLSError {
Ssl(SslError),
Io(io::Error),
}

pub type Result<T> = result::Result<T, CaesarError>;
pub type Result<T> = result::Result<T, FoxTLSError>;

impl fmt::Display for CaesarError {
impl fmt::Display for FoxTLSError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CaesarError::Ssl(ref e) => write!(f, "SSL error: {}", e),
CaesarError::Io(ref e) => write!(f, "IO error: {}", e),
FoxTLSError::Ssl(ref e) => write!(f, "SSL error: {}", e),
FoxTLSError::Io(ref e) => write!(f, "IO error: {}", e),
}
}
}

impl error::Error for CaesarError {
impl error::Error for FoxTLSError {
fn description(&self) -> &str {
match *self {
CaesarError::Ssl(ref e) => e.description(),
CaesarError::Io(ref e) => e.description(),
FoxTLSError::Ssl(ref e) => e.description(),
FoxTLSError::Io(ref e) => e.description(),
}
}

fn cause(&self) -> Option<&error::Error> {
match *self {
CaesarError::Ssl(ref e) => Some(e),
CaesarError::Io(ref e) => Some(e),
FoxTLSError::Ssl(ref e) => Some(e),
FoxTLSError::Io(ref e) => Some(e),
}
}
}

impl From<SslError> for CaesarError {
fn from(err: SslError) -> CaesarError {
CaesarError::Ssl(err)
impl From<SslError> for FoxTLSError {
fn from(err: SslError) -> FoxTLSError {
FoxTLSError::Ssl(err)
}
}

impl From<io::Error> for CaesarError {
fn from(err: io::Error) -> CaesarError {
CaesarError::Io(err)
impl From<io::Error> for FoxTLSError {
fn from(err: io::Error) -> FoxTLSError {
FoxTLSError::Io(err)
}
}