diff --git a/example/Cargo.toml b/example/Cargo.toml index 9ac384c6..9dbb378e 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -9,7 +9,5 @@ futures = "0.1" libp2p-secio = { path = "../libp2p-secio" } libp2p-swarm = { path = "../libp2p-swarm" } libp2p-tcp-transport = { path = "../libp2p-tcp-transport" } -ring = { version = "0.12.1", features = ["rsa_signing"] } tokio-core = "0.1" tokio-io = "0.1" -untrusted = "0.6" diff --git a/example/examples/echo-dialer.rs b/example/examples/echo-dialer.rs index 6964c257..7e7730b6 100644 --- a/example/examples/echo-dialer.rs +++ b/example/examples/echo-dialer.rs @@ -23,24 +23,19 @@ extern crate futures; extern crate libp2p_secio as secio; extern crate libp2p_swarm as swarm; extern crate libp2p_tcp_transport as tcp; -extern crate ring; extern crate tokio_core; extern crate tokio_io; -extern crate untrusted; use bytes::Bytes; use futures::future::{Future, FutureResult, IntoFuture}; use futures::{Stream, Sink}; -use ring::signature::RSAKeyPair; use std::io::Error as IoError; use std::iter; -use std::sync::Arc; use swarm::{Transport, ConnectionUpgrade}; use tcp::TcpConfig; use tokio_core::reactor::Core; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::length_delimited; -use untrusted::Input; fn main() { let mut core = Core::new().unwrap(); @@ -49,15 +44,10 @@ fn main() { let with_secio = tcp .with_upgrade(swarm::PlainTextConfig) .or_upgrade({ - let private_key = { - let pkcs8 = include_bytes!("test-private-key.pk8"); - Arc::new(RSAKeyPair::from_pkcs8(Input::from(&pkcs8[..])).unwrap()) - }; + let private_key = include_bytes!("test-private-key.pk8"); let public_key = include_bytes!("test-public-key.der").to_vec(); - - secio::SecioConnUpgrade { - local_public_key: public_key, - local_private_key: private_key, + secio::SecioConfig { + key: secio::SecioKeyPair::rsa_from_pkcs8(private_key, public_key).unwrap(), } }); diff --git a/example/examples/echo-server.rs b/example/examples/echo-server.rs index d4f53e32..12e63f6b 100644 --- a/example/examples/echo-server.rs +++ b/example/examples/echo-server.rs @@ -23,24 +23,19 @@ extern crate futures; extern crate libp2p_secio as secio; extern crate libp2p_swarm as swarm; extern crate libp2p_tcp_transport as tcp; -extern crate ring; extern crate tokio_core; extern crate tokio_io; -extern crate untrusted; use bytes::Bytes; use futures::future::{Future, FutureResult, IntoFuture, loop_fn, Loop}; use futures::{Stream, Sink}; -use ring::signature::RSAKeyPair; use std::io::Error as IoError; use std::iter; -use std::sync::Arc; use swarm::{Transport, ConnectionUpgrade}; use tcp::TcpConfig; use tokio_core::reactor::Core; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::codec::length_delimited; -use untrusted::Input; fn main() { let mut core = Core::new().unwrap(); @@ -49,15 +44,10 @@ fn main() { let with_secio = tcp .with_upgrade(swarm::PlainTextConfig) .or_upgrade({ - let private_key = { - let pkcs8 = include_bytes!("test-private-key.pk8"); - Arc::new(RSAKeyPair::from_pkcs8(Input::from(&pkcs8[..])).unwrap()) - }; + let private_key = include_bytes!("test-private-key.pk8"); let public_key = include_bytes!("test-public-key.der").to_vec(); - - secio::SecioConnUpgrade { - local_public_key: public_key, - local_private_key: private_key, + secio::SecioConfig { + key: secio::SecioKeyPair::rsa_from_pkcs8(private_key, public_key).unwrap(), } }); diff --git a/libp2p-secio/src/lib.rs b/libp2p-secio/src/lib.rs index a547b651..e2b06e6c 100644 --- a/libp2p-secio/src/lib.rs +++ b/libp2p-secio/src/lib.rs @@ -29,7 +29,7 @@ //! `SecioMiddleware` that implements `Sink` and `Stream` and can be used to send packets of data. //! //! However for integration with the rest of `libp2p` you are encouraged to use the -//! `SecioConnUpgrade` struct instead. This struct implements the `ConnectionUpgrade` trait and +//! `SecioConfig` struct instead. This struct implements the `ConnectionUpgrade` trait and //! will automatically apply secio on any incoming or outgoing connection. extern crate bytes; @@ -50,10 +50,12 @@ use futures::{Future, Poll, StartSend, Sink, Stream}; use futures::stream::MapErr as StreamMapErr; use ring::signature::RSAKeyPair; use rw_stream_sink::RwStreamSink; +use std::error::Error; use std::io::{Error as IoError, ErrorKind as IoErrorKind}; use std::iter; use std::sync::Arc; use tokio_io::{AsyncRead, AsyncWrite}; +use untrusted::Input; mod algo_support; mod codec; @@ -62,18 +64,74 @@ mod keys_proto; mod handshake; mod structs_proto; -/// Implementation of the `ConnectionUpgrade` trait of `libp2p_swarm`. Automatically applies any +/// Implementation of the `ConnectionUpgrade` trait of `libp2p_swarm`. Automatically applies /// secio on any connection. #[derive(Clone)] -pub struct SecioConnUpgrade { - /// Public key of the local node. Must match `local_private_key` or an error will happen during - /// the handshake. - pub local_public_key: Vec, - /// Private key that will be used to prove the identity of the local node. - pub local_private_key: Arc, +pub struct SecioConfig { + /// Private and public keys of the local node. + pub key: SecioKeyPair, } -impl libp2p_swarm::ConnectionUpgrade for SecioConnUpgrade +/// Private and public keys of the local node. +/// +/// # Generating offline keys with OpenSSL +/// +/// ## RSA +/// +/// Generating the keys: +/// +/// ```ignore +/// openssl genrsa -out private.pem 2048 +/// openssl rsa -in private.pem -outform DER -pubout -out public.der +/// openssl pkcs8 -in private.pem -topk8 -nocrypt -out private.pk8 +/// rm private.pem # optional +/// ``` +/// +/// Loading the keys: +/// +/// ```ignore +/// let key_pair = SecioKeyPair::rsa_from_pkcs8(include_bytes!("private.pk8"), +/// include_bytes!("public.der")); +/// ``` +/// +#[derive(Clone)] +pub struct SecioKeyPair { + inner: SecioKeyPairInner, +} + +impl SecioKeyPair { + pub fn rsa_from_pkcs8

(private: &[u8], public: P) + -> Result> + where P: Into> + { + let private = RSAKeyPair::from_pkcs8(Input::from(&private[..])) + .map_err(|err| Box::new(err))?; + + Ok(SecioKeyPair { + inner: SecioKeyPairInner::Rsa { + public: public.into(), + private: Arc::new(private), + } + }) + } +} + +// Inner content of `SecioKeyPair`. +#[derive(Clone)] +enum SecioKeyPairInner { + Rsa { + public: Vec, + private: Arc, + } +} + +#[derive(Debug, Clone)] +pub enum SecioPublicKey<'a> { + /// DER format. + Rsa(&'a [u8]), +} + +impl libp2p_swarm::ConnectionUpgrade for SecioConfig where S: AsyncRead + AsyncWrite + 'static { type Output = RwStreamSink< @@ -95,8 +153,7 @@ impl libp2p_swarm::ConnectionUpgrade for SecioConnUpgrade fn upgrade(self, incoming: S, _: ()) -> Self::Future { let fut = SecioMiddleware::handshake( incoming, - self.local_public_key.clone(), - self.local_private_key.clone(), + self.key, ); let wrapped = fut.map(|stream_sink| { let mapped = stream_sink.map_err(map_err as fn(_) -> _); @@ -125,19 +182,17 @@ impl SecioMiddleware { /// Attempts to perform a handshake on the given socket. /// - /// `local_public_key` and `local_private_key` must match. `local_public_key` must be in the - /// DER format. - /// /// On success, produces a `SecioMiddleware` that can then be used to encode/decode /// communications. pub fn handshake<'a>( socket: S, - local_public_key: Vec, - local_private_key: Arc, + key_pair: SecioKeyPair, ) -> Box, Error = SecioError> + 'a> where S: 'a { - let fut = handshake::handshake(socket, local_public_key, local_private_key) + let SecioKeyPairInner::Rsa { private, public } = key_pair.inner; + + let fut = handshake::handshake(socket, public, private) .map(|(inner, pubkey)| { SecioMiddleware { inner: inner, @@ -149,8 +204,8 @@ impl SecioMiddleware /// Returns the public key of the remote in the `DER` format. #[inline] - pub fn remote_public_key_der(&self) -> &[u8] { - &self.remote_pubkey_der + pub fn remote_public_key_der(&self) -> SecioPublicKey { + SecioPublicKey::Rsa(&self.remote_pubkey_der) } }