commit: | ddebe6894b45641840f9e39eac83282e9f76af1d |
author: | Trevor Bentley |
committer: | Trevor Bentley |
date: | Mon May 27 22:33:21 2019 +0200 |
parents: | a21387231528fddf2f9da9446c7c80121fab974c |
diff --git a/Cargo.toml b/Cargo.toml line changes: +1/-1 index 768267f..09a9921 --- a/Cargo.toml +++ b/Cargo.toml
@@ -21,5 +21,5 @@ overflow-checks = false [dependencies] x25519-dalek = "=0.5.2" ed25519-dalek = "=1.0.0-pre.1" -chacha20-poly1305-aead = "=0.1.2" +chacha20-poly1305-aead = { version = "=0.1.2", features= ["simd", "simd_opt"] } rand = "=0.6.5"
diff --git a/Xargo.toml b/Xargo.toml line changes: +22/-0 index 0000000..95d07d3 --- /dev/null +++ b/Xargo.toml
@@ -0,0 +1,22 @@ +# Xargo.toml +# +# Uses Xargo to build a local copy of Rust's libstd optimized for size, which +# allows for better LTO optimization in the final Ossuary release library. +# Doing this shaves about 100KB off of the shared lib. +# +# 'cargo install xargo' and 'rustup component add rust-src' if you need it. +# Only works with nightly. +# +[dependencies] +std = {default-features=false, features=["panic_immediate_abort"]} + +[profile.release] +panic = "abort" +opt-level = "z" +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 +incremental = true +overflow-checks = false
diff --git a/build_all_and_test.sh b/build_all_and_test.sh line changes: +28/-0 index 0000000..46b2543 --- /dev/null +++ b/build_all_and_test.sh
@@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +cargo test +cargo bench -- --nocapture +cargo build --examples +cargo build --release --examples +xargo build --target x86_64-apple-darwin --release +strip -u -r -x -S target/release/libossuary.dylib +strip -u -r -x -S target/x86_64-apple-darwin/release/libossuary.dylib +mkdir -p examples/build/ +pushd examples/build > /dev/null +cmake .. +make +popd > /dev/null + +echo "" +echo "Debug build:" +ls target/debug/*.dylib + +echo "" +echo "Release build:" +ls target/x86_64-apple-darwin/release/*.dylib + +echo "" +echo "Examples:" +find target/debug/examples/ -type f -perm +111 -d 1 +find examples/build/ -type f -perm +111 -d 1
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt line changes: +22/-10 index 880c3f4..580b59c --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt
@@ -1,20 +1,32 @@ cmake_minimum_required(VERSION 3.12) project(ffi C) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto -O0 -g") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto -O3") +option(LINK_STATIC "Link against static library" false) add_executable (ffi ffi.c) target_include_directories (ffi PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ffi) -target_link_libraries (ffi LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.dylib) - -#target_link_libraries (ffi LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.a) -#find_library(SECURITY_FRAMEWORK Security) -#target_link_libraries(ffi LINK_PUBLIC ${SECURITY_FRAMEWORK}) +if (LINK_STATIC) + target_link_libraries (ffi LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.a) + find_library(SECURITY_FRAMEWORK Security) + target_link_libraries(ffi LINK_PUBLIC ${SECURITY_FRAMEWORK}) +else() + target_link_libraries (ffi LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.dylib) +endif() add_executable (client client.c) -target_include_directories (client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ffi) -target_link_libraries (client LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.dylib) - add_executable (server server.c) +target_include_directories (client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ffi) target_include_directories (server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ffi) -target_link_libraries (server LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.dylib) + +if (LINK_STATIC) + target_link_libraries (client LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.a) + target_link_libraries (server LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.a) + #target_link_libraries (server LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/x86_64-apple-darwin/release/libossuary.a) + find_library(SECURITY_FRAMEWORK Security) + target_link_libraries(client LINK_PUBLIC ${SECURITY_FRAMEWORK}) + target_link_libraries(server LINK_PUBLIC ${SECURITY_FRAMEWORK}) +else() + target_link_libraries (client LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.dylib) + target_link_libraries (server LINK_PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../target/release/libossuary.dylib) +endif()
diff --git a/examples/example.rs b/examples/example.rs line changes: +77/-0 index 0000000..6e8a12b --- /dev/null +++ b/examples/example.rs
@@ -0,0 +1,77 @@ +// example.rs +// +// Basic example of Ossuary communication library, without authentication +// +// Establishes a non-authenticated session between a client and server over a +// TCP connection, and exchanges encrypted messages. +// +use ossuary::{OssuaryConnection, ConnectionType}; +use ossuary::OssuaryError; + +use std::thread; +use std::net::{TcpListener, TcpStream}; + +fn event_loop(mut conn: OssuaryConnection, + mut stream: TcpStream, + is_server: bool) -> Result<(), std::io::Error> { + let mut strings = vec!("message1", "message2", "message3"); + let mut plaintext = Vec::<u8>::new(); + let start = std::time::Instant::now(); + + // Simply run for 2 seconds + while start.elapsed().as_secs() < 2 { + match conn.handshake_done() { + // Handshaking + Ok(false) => { + conn.send_handshake(&mut stream).expect("handshake failed"); + conn.recv_handshake(&mut stream).expect("handshake failed"); + }, + // Transmitting on encrypted connection + Ok(true) => { + if let Some(plaintext) = strings.pop() { + let _ = conn.send_data(plaintext.as_bytes(), &mut stream); + } + if let Ok(_) = conn.recv_data(&mut stream, &mut plaintext) { + println!("({}) received: {:?}", is_server, + String::from_utf8(plaintext.clone()).unwrap()); + plaintext.clear(); + } + }, + // Trust-On-First-Use + Err(OssuaryError::UntrustedServer(pubkey)) => { + let keys: Vec<&[u8]> = vec![&pubkey]; + let _ = conn.add_authorized_keys(keys).unwrap(); + } + // Uh-oh. + Err(e) => panic!("Handshake failed with error: {:?}", e), + } + } + Ok(()) +} + +fn server() -> Result<(), std::io::Error> { + let listener = TcpListener::bind("127.0.0.1:9988").unwrap(); + let stream: TcpStream = listener.incoming().next().unwrap().unwrap(); + let _ = stream.set_read_timeout(Some(std::time::Duration::from_millis(100u64))); + // This server lets any client connect + let conn = OssuaryConnection::new(ConnectionType::UnauthenticatedServer, None).unwrap(); + let _ = event_loop(conn, stream, true); + Ok(()) +} + +fn client() -> Result<(), std::io::Error> { + let stream = TcpStream::connect("127.0.0.1:9988").unwrap(); + let _ = stream.set_read_timeout(Some(std::time::Duration::from_millis(100u64))); + // This client doesn't know any servers, but will use Trust-On-First-Use + let conn = OssuaryConnection::new(ConnectionType::Client, None).unwrap(); + let _ = event_loop(conn, stream, false); + Ok(()) +} + +fn main() { + let server = thread::spawn(move || { let _ = server(); }); + std::thread::sleep(std::time::Duration::from_millis(500)); + let child = thread::spawn(move || { let _ = client(); }); + let _ = child.join(); + let _ = server.join(); +}
diff --git a/src/clib.rs b/src/clib.rs line changes: +2/-0 index 96fec85..5c00f36 --- a/src/clib.rs +++ b/src/clib.rs
@@ -1,3 +1,5 @@ +//! Ossuary API exposed with a C FFI +//! use crate::{OssuaryConnection, ConnectionType, OssuaryError, KEY_LEN, generate_auth_keypair}; pub const OSSUARY_ERR_WOULD_BLOCK: i32 = -64;
diff --git a/src/connection.rs b/src/connection.rs line changes: +7/-3 index 4dec8b5..d7549b4 --- a/src/connection.rs +++ b/src/connection.rs
@@ -1,6 +1,5 @@ use crate::*; -//use rand::thread_rng; use rand::RngCore; use rand::rngs::OsRng;
@@ -27,7 +26,9 @@ impl OssuaryConnection { /// /// `auth_secret_key` is the secret portion of the long-term Ed25519 key /// used for host authentication. If `None` is provided, a keypair will - /// be generated for the lifetime of this connection object. + /// be generated for the lifetime of this connection object. This key + /// can be changed with [`OssuaryConnection::set_secret_key`]. + /// pub fn new(conn_type: ConnectionType, auth_secret_key: Option<&[u8]>) -> Result<OssuaryConnection, OssuaryError> { let mut rng = OsRng::new().expect("RNG not available.");
@@ -199,7 +200,10 @@ impl OssuaryConnection { } Ok(count) } - /// Add authentication secret signing key + /// Set authentication secret signing key + /// + /// Changes the secret authentication key of this side of the connection, + /// which was previously set by [`OssuaryConnection::new`] /// /// `key` must be a `&[u8]` slice containing a valid 32-byte ed25519 /// signing key. Signing keys should be kept secret and should be stored
diff --git a/src/error.rs b/src/error.rs line changes: +0/-2 index 44f5538..0a11e13 --- a/src/error.rs +++ b/src/error.rs
@@ -1,5 +1,3 @@ -use crate::*; - #[derive(Clone, PartialEq)] /// Error produced by Ossuary or one of its dependencies pub enum OssuaryError {
diff --git a/src/handshake.rs b/src/handshake.rs line changes: +11/-17 index df76521..f13ff8c --- a/src/handshake.rs +++ b/src/handshake.rs
@@ -2,6 +2,8 @@ use crate::*; use comm::{read_packet, write_packet, write_stored_packet}; +use std::io::Write; + const CLIENT_HANDSHAKE_PACKET_LEN: usize = CHALLENGE_LEN + NONCE_LEN + KEY_LEN + 8; const CLIENT_AUTH_PACKET_LEN: usize = CLIENT_AUTH_SUBPACKET_LEN + 8; const SERVER_HANDSHAKE_PACKET_LEN: usize = NONCE_LEN + KEY_LEN + SERVER_HANDSHAKE_SUBPACKET_LEN + 8;
@@ -123,8 +125,7 @@ impl ServerHandshakePacket { enc_pkt.public_key.copy_from_slice(server_pubkey); enc_pkt.challenge.copy_from_slice(challenge); enc_pkt.signature.copy_from_slice(signature); - let mut subpkt: &mut [u8] = &mut pkt.subpacket; - encrypt_to_bytes(session_privkey, nonce, struct_as_slice(&enc_pkt), &mut subpkt)?; + encrypt_to_bytes(session_privkey, nonce, struct_as_slice(&enc_pkt), &mut pkt.subpacket)?; Ok(pkt) } fn from_packet(pkt: &NetworkPacket) -> Result<&ServerHandshakePacket, OssuaryError> {
@@ -188,8 +189,7 @@ impl ClientAuthenticationPacket { let mut enc_pkt: ClientEncryptedAuthenticationPacket = Default::default(); enc_pkt.public_key.copy_from_slice(client_pubkey); enc_pkt.signature.copy_from_slice(signature); - let mut subpkt: &mut [u8] = &mut pkt.subpacket; - encrypt_to_bytes(session_privkey, nonce, struct_as_slice(&enc_pkt), &mut subpkt)?; + encrypt_to_bytes(session_privkey, nonce, struct_as_slice(&enc_pkt), &mut pkt.subpacket)?; Ok(pkt) } fn from_packet(pkt: &NetworkPacket) -> Result<&ClientAuthenticationPacket, OssuaryError> {
@@ -523,9 +523,8 @@ impl OssuaryConnection { return Err(OssuaryError::InvalidPacket("Received unexpected handshake packet.".into())); } }; - let mut pt: &mut [u8] = &mut plaintext; // note: pt is consumed by decrypt_to_bytes - match decrypt_to_bytes(session, &nonce, &inner_pkt.subpacket, &mut pt) { + match decrypt_to_bytes(session, &nonce, &inner_pkt.subpacket, &mut plaintext) { Ok(_) => {}, Err(e) => { self.reset_state(None);
@@ -622,9 +621,8 @@ impl OssuaryConnection { return Err(OssuaryError::InvalidPacket("Received unexpected handshake packet.".into())); } }; - let mut pt: &mut [u8] = &mut plaintext; // note: pt is consumed by decrypt_to_bytes - match decrypt_to_bytes(session, &nonce, &inner_pkt.subpacket, &mut pt) { + match decrypt_to_bytes(session, &nonce, &inner_pkt.subpacket, &mut plaintext) { Ok(_) => {}, Err(e) => { self.reset_state(None);
@@ -735,10 +733,8 @@ impl OssuaryConnection { } } -fn encrypt_to_bytes<T,U>(session_key: &[u8], nonce: &[u8], - data: &[u8], mut out: T) -> Result<usize, OssuaryError> -where T: std::ops::DerefMut<Target = U>, - U: std::io::Write { +fn encrypt_to_bytes(session_key: &[u8], nonce: &[u8], + data: &[u8], mut out: &mut [u8]) -> Result<usize, OssuaryError> { let aad = []; let mut ciphertext = Vec::with_capacity(data.len()); let tag = match encrypt(session_key,
@@ -762,10 +758,8 @@ where T: std::ops::DerefMut<Target = U>, Ok(size) } -fn decrypt_to_bytes<T,U>(session_key: &[u8], nonce: &[u8], - data: &[u8], mut out: T) -> Result<usize, OssuaryError> -where T: std::ops::DerefMut<Target = U>, - U: std::io::Write { +fn decrypt_to_bytes(session_key: &[u8], nonce: &[u8], + data: &[u8], mut out: &mut [u8]) -> Result<usize, OssuaryError> { let s: &EncryptedPacket = slice_as_struct(data)?; if s.tag_len != 16 { return Err(OssuaryError::InvalidPacket("Invalid packet length".into()));
@@ -778,6 +772,6 @@ where T: std::ops::DerefMut<Target = U>, decrypt(session_key, &nonce, &aad, &ciphertext, &tag, - out.deref_mut())?; + &mut out)?; Ok(ciphertext.len()) }
diff --git a/src/lib.rs b/src/lib.rs line changes: +0/-5 index b521cae..268f994 --- a/src/lib.rs +++ b/src/lib.rs
@@ -135,11 +135,6 @@ mod error; pub use error::OssuaryError; -extern crate x25519_dalek; -extern crate ed25519_dalek; -extern crate rand; -extern crate chacha20_poly1305_aead; - use chacha20_poly1305_aead::{encrypt,decrypt}; use x25519_dalek::{EphemeralSecret, PublicKey as EphemeralPublic, SharedSecret}; use ed25519_dalek::{Signature, Keypair, SecretKey, PublicKey};