Skip to content
This repository has been archived by the owner on Nov 28, 2020. It is now read-only.

Commit

Permalink
Added curve libsodium linking & feature flag (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
jean-airoldie authored Sep 2, 2019
1 parent e5378ce commit c2ea5bc
Show file tree
Hide file tree
Showing 24 changed files with 405 additions and 84 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ before_script:
- (test -x $HOME/.cargo/bin/cargo-cache || cargo install cargo-cache)
- (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.3" mdbook)
script:
- cargo test --all-targets --no-run
- cargo test --all
- cargo test --examples
- cargo test --all-targets --all-features --no-run
- cargo test --all --all-features
- cargo test --examples --all-features
- cargo fmt --all -- --check
- cargo clippy --all-targets -- -D warnings
- mdbook build libzmq-book
Expand Down
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@
A strict subset of ØMQ with an ergonomic API.

# Versioning & Stability Guarantees
**Expect breaking changes with each release until `0.2`**. After that, expect breaking changes only on minor
version until `1.0`. Furthermore, since a large part of the library relies on ØMQ's DRAFT API,
they will have to be stabilized before the 1.0 version is released.

```toml
[dependencies]
libzmq = "0.1.24"
libzmq = "0.2"
```

# Dead Simple Sample
Expand Down
9 changes: 6 additions & 3 deletions libzmq-book/src/examples/secure_req_rep.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ For a public `TCP` connection, its a must. Let's fix that by adapting the
previous example.

This time we will the `CURVE` mechanism, which is a public-key crypto.
Depending on your setup, it should have little to no overhead.
It might even boost throughtput in case where the NIC is the bottleneck
because of the extra compression.
To enable the usage of the `CURVE` mechanism, the feature flag 'curve'
must be enabled.

However, this time we will use an external configuration file to get
rid of all the boilerplate. This will also allows our application
to run indepently of the socket configuration.

Based on some basic benchmarks, the `CURVE` mechanism
might reduce the throughtput of I/O heavy applications by half due
to the overhead of the `salsa20` encryption.

## Config File

In this case we used `yaml` configuration file, but any file format
Expand Down
8 changes: 5 additions & 3 deletions libzmq-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libzmq-sys"
version = "0.1.6"
version = "0.1.7+4.3.2"
authors = ["jean-airoldie <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand All @@ -20,15 +20,17 @@ maintenance = { status = "actively-developed" }
[features]
# Renew the pre-generated `libzmq` bindings.
renew-bindings = ['bindgen']
libsodium = ['libsodium-sys']

[dependencies]
libc = "0.2"
libsodium-sys = { version = "0.2.3", optional = true }

[dev-dependencies]
version-sync = "0.8"

[build-dependencies]
cmake = "0.1"
bindgen = { version = "0.49.0", optional = true }
# Use `libzmq` version 4.3.2 which is still a dev preview.
zeromq-src = "0.1.7"
# libzmq 4.3.2
zeromq-src = "0.1.8"
12 changes: 12 additions & 0 deletions libzmq-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,22 @@ fn main() {

let wants_debug = env::var_os("PROFILE").unwrap() == "debug";

let maybe_libsodium = if cfg!(feature = "libsodium") {
let lib_dir = env::var("DEP_SODIUM_LIB")
.expect("build metadata `DEP_SODIUM_LIB` required");
let include_dir = env::var("DEP_SODIUM_INCLUDE")
.expect("build metadata `DEP_SODIUM_INCLUDE` required");

Some(zeromq_src::LibLocation::new(lib_dir, include_dir))
} else {
None
};

let artifacts = zeromq_src::Build::new()
.link_static(true)
.enable_draft(true)
.build_debug(wants_debug)
.with_libsodium(maybe_libsodium)
.build();

artifacts.print_cargo_metadata();
Expand Down
2 changes: 1 addition & 1 deletion libzmq-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Ignore generated code
#![allow(clippy::all)]
#![doc(html_root_url = "https://docs.rs/libzmq-sys/0.1.6")]
#![doc(html_root_url = "https://docs.rs/libzmq-sys/0.1.7")]

//! libzmq-sys - Raw cFFI bindings to [libzmq](https://github.com/zeromq/libzmq).
Expand Down
14 changes: 10 additions & 4 deletions libzmq/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libzmq"
version = "0.1.24"
version = "0.2.0"
authors = ["jean-airoldie <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand All @@ -17,16 +17,18 @@ homepage = "https://jean-airoldie.github.io/libzmq-rs/"
[badges]
maintenance = { status = "actively-developed" }

[features]
curve = ['libzmq-sys/libsodium']

[dependencies]
libc = "0.2"
serde = { version = "1.0", features = ["derive"] }
humantime-serde = "0.1"
serde_with = "1.3.1"
lazy_static = "1.3.0"
failure = "0.1"
libzmq-sys = { path = "../libzmq-sys", version = "0.1.6" }
libzmq-sys = { path = "../libzmq-sys", version = "0.1.7" }
bitflags = "1.0"
hashbrown = "0.2"
log = "0.4"
uuid = { version = "0.7", features = ["v4"] }
bincode = "1.1"
Expand All @@ -38,13 +40,17 @@ rand_isaac = "0.1.1"
rand_core = "0.4"
criterion = "0.2"
version-sync = "0.8"
ron = "0.5"
quickcheck = "0.8.3"
serde_yaml = "0.8"

[build-dependencies]
flatc-rust = "0.1"

[[example]]
name = "secure_req_rep"
required-features = ['curve']

[[bench]]
required-features = ['curve']
name = "bench_main"
harness = false
6 changes: 3 additions & 3 deletions libzmq/benches/bench_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use criterion::{criterion_group, criterion_main};

use std::time::Duration;

const MSG_AMOUNT: usize = 100_000;
const MSG_SIZE: usize = 50;
const HWM: i32 = 1_000;
const MSG_AMOUNT: usize = 50;
const MSG_SIZE: usize = 500_000; // 0.5MB messages
const HWM: i32 = 10;
const SAMPLE_SIZE: usize = 10;
const MEASUREMENT_TIME: Duration = Duration::from_secs(30);

Expand Down
59 changes: 32 additions & 27 deletions libzmq/src/auth/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,42 +44,44 @@ fn into_ipv6(ip: IpAddr) -> Ipv6Addr {
/// # use failure::Error;
/// #
/// # fn main() -> Result<(), Error> {
/// use libzmq::{prelude::*, auth::*, *};
/// use std::{time::Duration};
/// #[cfg(feature = "curve")] {
/// use libzmq::{prelude::*, auth::*, *};
/// use std::{time::Duration};
///
/// let server_cert = CurveCert::new_unique();
/// let client_cert = CurveCert::new_unique();
/// let server_cert = CurveCert::new_unique();
/// let client_cert = CurveCert::new_unique();
///
/// let addr: TcpAddr = "127.0.0.1:*".try_into()?;
/// let addr: TcpAddr = "127.0.0.1:*".try_into()?;
///
/// let server_creds = CurveServerCreds::new(server_cert.secret());
/// let server_creds = CurveServerCreds::new(server_cert.secret());
///
/// // Creates a server using the `CurveServer` mechanism. Since `CURVE`
/// // authentication is enabled by default, only sockets whose public key
/// // is in the whitelist will be allowed to connect.
/// let server = ServerBuilder::new()
/// .bind(&addr)
/// .mechanism(server_creds)
/// .recv_timeout(Duration::from_millis(200))
/// .build()?;
/// // Creates a server using the `CurveServer` mechanism. Since `CURVE`
/// // authentication is enabled by default, only sockets whose public key
/// // is in the whitelist will be allowed to connect.
/// let server = ServerBuilder::new()
/// .bind(&addr)
/// .mechanism(server_creds)
/// .recv_timeout(Duration::from_millis(200))
/// .build()?;
///
/// // We need to tell the `AuthServer` to allow the client's public key.
/// let _ = AuthBuilder::new().curve_registry(client_cert.public()).build()?;
/// // We need to tell the `AuthServer` to allow the client's public key.
/// let _ = AuthBuilder::new().curve_registry(client_cert.public()).build()?;
///
/// let bound = server.last_endpoint()?;
/// let bound = server.last_endpoint()?;
///
/// let client_creds = CurveClientCreds::new(server_cert.public())
/// .add_cert(client_cert);
/// let client_creds = CurveClientCreds::new(server_cert.public())
/// .add_cert(client_cert);
///
/// // Creates a server using the `CurveServer` mechanism. Since `CURVE`
/// let client = ClientBuilder::new()
/// .mechanism(client_creds)
/// .connect(bound)
/// .build()?;
/// // Creates a server using the `CurveServer` mechanism. Since `CURVE`
/// let client = ClientBuilder::new()
/// .mechanism(client_creds)
/// .connect(bound)
/// .build()?;
///
/// // The handshake is successfull so we can now send and receive messages.
/// client.send("").unwrap();
/// server.recv_msg().unwrap();
/// // The handshake is successfull so we can now send and receive messages.
/// client.send("").unwrap();
/// server.recv_msg().unwrap();
/// }
/// #
/// # Ok(())
/// # }
Expand Down Expand Up @@ -629,6 +631,7 @@ mod test {
}

#[test]
#[cfg(feature = "curve")]
fn test_curve() {
// Create a new context to use a disctinct auth handler.
let ctx = Ctx::new();
Expand Down Expand Up @@ -671,6 +674,7 @@ mod test {
}

#[test]
#[cfg(feature = "curve")]
fn test_curve_denied() {
let addr: TcpAddr = "127.0.0.1:*".try_into().unwrap();

Expand Down Expand Up @@ -699,6 +703,7 @@ mod test {
}

#[test]
#[cfg(feature = "curve")]
fn test_curve_no_auth() {
// Create a new context to use a disctinct auth handler.
let ctx = Ctx::new();
Expand Down
119 changes: 119 additions & 0 deletions libzmq/src/auth/curve.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// The z85 codec logic is largely based on https://github.com/decafbad/z85
use super::Mechanism;
use crate::prelude::TryFrom;

use libzmq_sys as sys;
Expand Down Expand Up @@ -628,6 +629,124 @@ impl<'a> From<&'a CurveSecretKey> for BinCurveKey {
}
}

/// Credentials for a `Curve` client.
///
/// # Example
/// ```
/// use libzmq::auth::*;
///
/// let server_cert = CurveCert::new_unique();
/// let client_cert = CurveCert::new_unique();
///
/// let creds = CurveClientCreds::new(server_cert.public())
/// .add_cert(client_cert);
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CurveClientCreds {
pub(crate) client: Option<CurveCert>,
pub(crate) server: CurvePublicKey,
}

impl CurveClientCreds {
/// Create a new `CurveClientCreds` from server's `CurvePublicKey`.
pub fn new<S>(server: S) -> Self
where
S: Into<CurvePublicKey>,
{
Self {
client: None,
server: server.into(),
}
}

/// Associates a client `CurveCert` with the credentials.
pub fn add_cert<C>(mut self, client: C) -> Self
where
C: Into<CurveCert>,
{
self.client = Some(client.into());
self
}

/// Returns a reference to the client certificate.
pub fn cert(&self) -> Option<&CurveCert> {
self.client.as_ref()
}

/// Returns a reference to the server public key.
pub fn server(&self) -> &CurvePublicKey {
&self.server
}
}

impl<'a> From<&'a CurveClientCreds> for CurveClientCreds {
fn from(creds: &'a CurveClientCreds) -> Self {
creds.to_owned()
}
}

impl<'a> From<&'a CurveClientCreds> for Mechanism {
fn from(creds: &'a CurveClientCreds) -> Self {
Mechanism::CurveClient(creds.to_owned())
}
}

impl From<CurveClientCreds> for Mechanism {
fn from(creds: CurveClientCreds) -> Self {
Mechanism::CurveClient(creds)
}
}

/// Credentials for a `Curve` server.
/// # Example
/// ```
/// use libzmq::auth::*;
///
/// let server_cert = CurveCert::new_unique();
///
/// let creds = CurveServerCreds::new(server_cert.secret());
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CurveServerCreds {
/// The server's `CurveSecretKey`.
pub(crate) secret: CurveSecretKey,
}

impl CurveServerCreds {
/// Create a new `CurveServerCreds` from a server secret `CurveSecretKey`.
pub fn new<S>(secret: S) -> Self
where
S: Into<CurveSecretKey>,
{
Self {
secret: secret.into(),
}
}

/// Returns a reference to the server secret key.
pub fn secret(&self) -> &CurveSecretKey {
&self.secret
}
}

impl<'a> From<&'a CurveServerCreds> for CurveServerCreds {
fn from(creds: &'a CurveServerCreds) -> Self {
creds.to_owned()
}
}

impl<'a> From<&'a CurveServerCreds> for Mechanism {
fn from(creds: &'a CurveServerCreds) -> Self {
Mechanism::CurveServer(creds.to_owned())
}
}

impl From<CurveServerCreds> for Mechanism {
fn from(creds: CurveServerCreds) -> Self {
Mechanism::CurveServer(creds)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading

0 comments on commit c2ea5bc

Please sign in to comment.