Clean up directory structure (#426)

* Remove unused circular-buffer crate
* Move transports into subdirectory
* Move misc into subdirectory
* Move stores into subdirectory
* Move multiplexers
* Move protocols
* Move libp2p top layer
* Fix Test: skip doctest if secio isn't enabled
This commit is contained in:
Benjamin Kampmann
2018-08-29 11:24:44 +02:00
committed by GitHub
parent f5ce93c730
commit 2ea49718f3
131 changed files with 146 additions and 1023 deletions

View File

@ -0,0 +1,95 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//! This module contains some utilities for algorithm support exchange.
//!
//! One important part of the SECIO handshake is negotiating algorithms. This is what this module
//! helps you with.
macro_rules! supported_impl {
($mod_name:ident: $ty:ty, $($name:expr => $val:expr),*,) => (
pub mod $mod_name {
use std::cmp::Ordering;
#[allow(unused_imports)]
use stream_cipher::KeySize;
#[allow(unused_imports)]
use ring::{agreement, digest};
use error::SecioError;
/// String to advertise to the remote.
pub const PROPOSITION_STRING: &'static str = concat_comma!($($name),*);
/// Choose which algorithm to use based on the remote's advertised list.
pub fn select_best(hashes_ordering: Ordering, input: &str) -> Result<$ty, SecioError> {
match hashes_ordering {
Ordering::Less | Ordering::Equal => {
for second_elem in input.split(',') {
$(
if $name == second_elem {
return Ok($val);
}
)+
}
},
Ordering::Greater => {
$(
for second_elem in input.split(',') {
if $name == second_elem {
return Ok($val);
}
}
)+
},
};
Err(SecioError::NoSupportIntersection(PROPOSITION_STRING, input.to_owned()))
}
}
);
}
// Concatenates several strings with commas.
macro_rules! concat_comma {
($first:expr, $($rest:expr),*) => (
concat!($first $(, ',', $rest)*)
);
}
// TODO: there's no library in the Rust ecosystem that supports P-521, but the Go & JS
// implementations advertise it
supported_impl!(
exchanges: &'static agreement::Algorithm,
"P-256" => &agreement::ECDH_P256,
"P-384" => &agreement::ECDH_P384,
);
// TODO: the Go & JS implementations advertise Blowfish ; however doing so in Rust leads to
// runtime errors
supported_impl!(
ciphers: KeySize,
"AES-128" => KeySize::KeySize128,
"AES-256" => KeySize::KeySize256,
);
supported_impl!(
hashes: &'static digest::Algorithm,
"SHA256" => &digest::SHA256,
"SHA512" => &digest::SHA512,
);

View File

@ -0,0 +1,128 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//! Individual messages decoding.
use bytes::BytesMut;
use super::StreamCipher;
use error::SecioError;
use futures::sink::Sink;
use futures::stream::Stream;
use futures::Async;
use futures::Poll;
use futures::StartSend;
use ring::hmac;
/// Wraps around a `Stream<Item = BytesMut>`. The buffers produced by the underlying stream
/// are decoded using the cipher and hmac.
///
/// This struct implements `Stream`, whose stream item are frames of data without the length
/// prefix. The mechanism for removing the length prefix and splitting the incoming data into
/// frames isn't handled by this module.
///
/// Also implements `Sink` for convenience.
pub struct DecoderMiddleware<S> {
cipher_state: StreamCipher,
hmac_key: hmac::VerificationKey,
// TODO: when a new version of ring is released, we can use `hmac_key.digest_algorithm().output_len` instead
hmac_num_bytes: usize,
raw_stream: S,
}
impl<S> DecoderMiddleware<S> {
#[inline]
pub fn new(
raw_stream: S,
cipher: StreamCipher,
hmac_key: hmac::VerificationKey,
hmac_num_bytes: usize, // TODO: remove this parameter
) -> DecoderMiddleware<S> {
DecoderMiddleware {
cipher_state: cipher,
hmac_key,
raw_stream,
hmac_num_bytes,
}
}
}
impl<S> Stream for DecoderMiddleware<S>
where
S: Stream<Item = BytesMut>,
S::Error: Into<SecioError>,
{
type Item = Vec<u8>;
type Error = SecioError;
#[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let frame = match self.raw_stream.poll() {
Ok(Async::Ready(Some(t))) => t,
Ok(Async::Ready(None)) => return Ok(Async::Ready(None)),
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(err) => return Err(err.into()),
};
// TODO: when a new version of ring is released, we can use `hmac_key.digest_algorithm().output_len` instead
let hmac_num_bytes = self.hmac_num_bytes;
if frame.len() < hmac_num_bytes {
debug!("frame too short when decoding secio frame");
return Err(SecioError::FrameTooShort);
}
let content_length = frame.len() - hmac_num_bytes;
{
let (crypted_data, expected_hash) = frame.split_at(content_length);
debug_assert_eq!(expected_hash.len(), hmac_num_bytes);
if hmac::verify(&self.hmac_key, crypted_data, expected_hash).is_err() {
debug!("hmac mismatch when decoding secio frame");
return Err(SecioError::HmacNotMatching);
}
}
let mut data_buf = frame.to_vec();
data_buf.truncate(content_length);
self.cipher_state
.try_apply_keystream(&mut data_buf)
.map_err::<SecioError,_>(|e|e.into())?;
Ok(Async::Ready(Some(data_buf)))
}
}
impl<S> Sink for DecoderMiddleware<S>
where
S: Sink,
{
type SinkItem = S::SinkItem;
type SinkError = S::SinkError;
#[inline]
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
self.raw_stream.start_send(item)
}
#[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
self.raw_stream.poll_complete()
}
}

View File

@ -0,0 +1,93 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//! Individual messages encoding.
use bytes::BytesMut;
use super::StreamCipher;
use futures::sink::Sink;
use futures::stream::Stream;
use futures::Poll;
use futures::StartSend;
use ring::hmac;
/// Wraps around a `Sink`. Encodes the buffers passed to it and passes it to the underlying sink.
///
/// This struct implements `Sink`. It expects individual frames of data, and outputs individual
/// frames as well, most notably without the length prefix. The mechanism for adding the length
/// prefix is not covered by this module.
///
/// Also implements `Stream` for convenience.
pub struct EncoderMiddleware<S> {
cipher_state: StreamCipher,
hmac_key: hmac::SigningKey,
raw_sink: S,
}
impl<S> EncoderMiddleware<S> {
pub fn new(
raw_sink: S,
cipher: StreamCipher,
hmac_key: hmac::SigningKey,
) -> EncoderMiddleware<S> {
EncoderMiddleware {
cipher_state: cipher,
hmac_key,
raw_sink,
}
}
}
impl<S> Sink for EncoderMiddleware<S>
where
S: Sink<SinkItem = BytesMut>,
{
type SinkItem = BytesMut;
type SinkError = S::SinkError;
fn start_send(&mut self, mut data_buf: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
// TODO if SinkError gets refactor to SecioError,
// then use try_apply_keystream
self.cipher_state.apply_keystream(&mut data_buf[..]);
let signature = hmac::sign(&self.hmac_key, &data_buf[..]);
data_buf.extend_from_slice(signature.as_ref());
self.raw_sink.start_send(data_buf)
}
#[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
self.raw_sink.poll_complete()
}
}
impl<S> Stream for EncoderMiddleware<S>
where
S: Stream,
{
type Item = S::Item;
type Error = S::Error;
#[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.raw_sink.poll()
}
}

View File

@ -0,0 +1,172 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//! Individual messages encoding and decoding. Use this after the algorithms have been
//! successfully negotiated.
use self::decode::DecoderMiddleware;
use self::encode::EncoderMiddleware;
use aes_ctr::stream_cipher::StreamCipherCore;
use ring::hmac;
use tokio_io::codec::length_delimited;
use tokio_io::{AsyncRead, AsyncWrite};
mod decode;
mod encode;
/// Type returned by `full_codec`.
pub type FullCodec<S> = DecoderMiddleware<EncoderMiddleware<length_delimited::Framed<S>>>;
pub type StreamCipher = Box<dyn StreamCipherCore + Send>;
/// Takes control of `socket`. Returns an object that implements `future::Sink` and
/// `future::Stream`. The `Stream` and `Sink` produce and accept `BytesMut` objects.
///
/// The conversion between the stream/sink items and the socket is done with the given cipher and
/// hash algorithm (which are generally decided during the handshake).
pub fn full_codec<S>(
socket: length_delimited::Framed<S>,
cipher_encoding: StreamCipher,
encoding_hmac: hmac::SigningKey,
cipher_decoder: StreamCipher,
decoding_hmac: hmac::VerificationKey,
) -> FullCodec<S>
where
S: AsyncRead + AsyncWrite,
{
let hmac_num_bytes = encoding_hmac.digest_algorithm().output_len;
let encoder = EncoderMiddleware::new(socket, cipher_encoding, encoding_hmac);
DecoderMiddleware::new(encoder, cipher_decoder, decoding_hmac, hmac_num_bytes)
}
#[cfg(test)]
mod tests {
extern crate tokio_current_thread;
extern crate tokio_tcp;
use self::tokio_tcp::TcpListener;
use self::tokio_tcp::TcpStream;
use stream_cipher::{ctr, KeySize};
use super::full_codec;
use super::DecoderMiddleware;
use super::EncoderMiddleware;
use bytes::BytesMut;
use error::SecioError;
use futures::sync::mpsc::channel;
use futures::{Future, Sink, Stream};
use rand;
use ring::digest::SHA256;
use ring::hmac::SigningKey;
use ring::hmac::VerificationKey;
use std::io::Error as IoError;
use tokio_io::codec::length_delimited::Framed;
const NULL_IV : [u8; 16] = [0;16];
#[test]
fn raw_encode_then_decode() {
let (data_tx, data_rx) = channel::<BytesMut>(256);
let data_tx = data_tx.sink_map_err::<_, IoError>(|_| panic!());
let data_rx = data_rx.map_err::<IoError, _>(|_| panic!());
let cipher_key: [u8; 32] = rand::random();
let hmac_key: [u8; 32] = rand::random();
let encoder = EncoderMiddleware::new(
data_tx,
ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]),
SigningKey::new(&SHA256, &hmac_key),
);
let decoder = DecoderMiddleware::new(
data_rx,
ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]),
VerificationKey::new(&SHA256, &hmac_key),
32,
);
let data = b"hello world";
let data_sent = encoder.send(BytesMut::from(data.to_vec())).from_err();
let data_received = decoder.into_future().map(|(n, _)| n).map_err(|(e, _)| e);
let (_, decoded) = tokio_current_thread::block_on_all(data_sent.join(data_received))
.map_err(|_| ())
.unwrap();
assert_eq!(&decoded.unwrap()[..], &data[..]);
}
#[test]
fn full_codec_encode_then_decode() {
let cipher_key: [u8; 32] = rand::random();
let cipher_key_clone = cipher_key.clone();
let hmac_key: [u8; 32] = rand::random();
let hmac_key_clone = hmac_key.clone();
let data = b"hello world";
let data_clone = data.clone();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming().into_future().map_err(|(e, _)| e).map(
move |(connec, _)| {
let connec = Framed::new(connec.unwrap());
full_codec(
connec,
ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]),
SigningKey::new(&SHA256, &hmac_key),
ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]),
VerificationKey::new(&SHA256, &hmac_key),
)
},
);
let client = TcpStream::connect(&listener_addr)
.map_err(|e| e.into())
.map(move |stream| {
let stream = Framed::new(stream);
full_codec(
stream,
ctr(KeySize::KeySize256, &cipher_key_clone, &NULL_IV[..]),
SigningKey::new(&SHA256, &hmac_key_clone),
ctr(KeySize::KeySize256, &cipher_key_clone, &NULL_IV[..]),
VerificationKey::new(&SHA256, &hmac_key_clone),
)
});
let fin = server
.join(client)
.from_err::<SecioError>()
.and_then(|(server, client)| {
client
.send(BytesMut::from(&data_clone[..]))
.map(move |_| server)
.from_err()
})
.and_then(|server| server.into_future().map_err(|(e, _)| e.into()))
.map(|recved| recved.0.unwrap().to_vec());
let received = tokio_current_thread::block_on_all(fin).unwrap();
assert_eq!(received, data);
}
}

View File

@ -0,0 +1,126 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//! Defines the `SecioError` enum that groups all possible errors in SECIO.
use aes_ctr::stream_cipher::LoopError;
use std::error;
use std::fmt;
use std::io::Error as IoError;
/// Error at the SECIO layer communication.
#[derive(Debug)]
pub enum SecioError {
/// I/O error.
IoError(IoError),
/// Failed to parse one of the handshake protobuf messages.
HandshakeParsingFailure,
/// There is no protocol supported by both the local and remote hosts.
NoSupportIntersection(&'static str, String),
/// Failed to generate nonce.
NonceGenerationFailed,
/// Failed to generate ephemeral key.
EphemeralKeyGenerationFailed,
/// Failed to sign a message with our local private key.
SigningFailure,
/// The signature of the exchange packet doesn't verify the remote public key.
SignatureVerificationFailed,
/// Failed to generate the secret shared key from the ephemeral key.
SecretGenerationFailed,
/// The final check of the handshake failed.
NonceVerificationFailed,
/// Error with block cipher.
CipherError(LoopError),
/// The received frame was of invalid length.
FrameTooShort,
/// The hashes of the message didn't match.
HmacNotMatching,
}
impl error::Error for SecioError {
#[inline]
fn description(&self) -> &str {
match *self {
SecioError::IoError(_) => "I/O error",
SecioError::HandshakeParsingFailure => {
"Failed to parse one of the handshake protobuf messages"
}
SecioError::NoSupportIntersection(_, _) => {
"There is no protocol supported by both the local and remote hosts"
}
SecioError::NonceGenerationFailed => "Failed to generate nonce",
SecioError::EphemeralKeyGenerationFailed => "Failed to generate ephemeral key",
SecioError::SigningFailure => "Failed to sign a message with our local private key",
SecioError::SignatureVerificationFailed => {
"The signature of the exchange packet doesn't verify the remote public key"
}
SecioError::SecretGenerationFailed => {
"Failed to generate the secret shared key from the ephemeral key"
}
SecioError::NonceVerificationFailed => "The final check of the handshake failed",
SecioError::CipherError(_) => "Error while decoding/encoding data",
SecioError::FrameTooShort => "The received frame was of invalid length",
SecioError::HmacNotMatching => "The hashes of the message didn't match",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
SecioError::IoError(ref err) => Some(err),
// TODO: The type doesn't implement `Error`
/*SecioError::CipherError(ref err) => {
Some(err)
},*/
_ => None,
}
}
}
impl fmt::Display for SecioError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<LoopError> for SecioError {
#[inline]
fn from(err: LoopError) -> SecioError {
SecioError::CipherError(err)
}
}
impl From<IoError> for SecioError {
#[inline]
fn from(err: IoError) -> SecioError {
SecioError::IoError(err)
}
}

View File

@ -0,0 +1,685 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use algo_support;
use bytes::BytesMut;
use codec::{full_codec, FullCodec};
use stream_cipher::{KeySize, ctr};
use error::SecioError;
use futures::future;
use futures::sink::Sink;
use futures::stream::Stream;
use futures::Future;
use libp2p_core::PublicKey;
use protobuf::parse_from_bytes as protobuf_parse_from_bytes;
use protobuf::Message as ProtobufMessage;
use ring::agreement::EphemeralPrivateKey;
use ring::hmac::{SigningContext, SigningKey, VerificationKey};
use ring::rand::SecureRandom;
use ring::signature::verify as signature_verify;
use ring::signature::{ED25519, RSASigningState, RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_SHA256};
use ring::{agreement, digest, rand};
#[cfg(feature = "secp256k1")]
use secp256k1;
use std::cmp::{self, Ordering};
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::mem;
use structs_proto::{Exchange, Propose};
use tokio_io::codec::length_delimited;
use tokio_io::{AsyncRead, AsyncWrite};
use untrusted::Input as UntrustedInput;
use {SecioKeyPair, SecioKeyPairInner};
/// Performs a handshake on the given socket.
///
/// This function expects that the remote is identified with `remote_public_key`, and the remote
/// will expect that we are identified with `local_key`.Any mismatch somewhere will produce a
/// `SecioError`.
///
/// On success, returns an object that implements the `Sink` and `Stream` trait whose items are
/// buffers of data, plus the public key of the remote, plus the ephemeral public key used during
/// negotiation.
pub fn handshake<'a, S: 'a>(
socket: S,
local_key: SecioKeyPair,
) -> Box<Future<Item = (FullCodec<S>, PublicKey, Vec<u8>), Error = SecioError> + 'a>
where
S: AsyncRead + AsyncWrite,
{
// TODO: could be rewritten as a coroutine once coroutines land in stable Rust
// This struct contains the whole context of a handshake, and is filled progressively
// throughout the various parts of the handshake.
struct HandshakeContext {
// Filled with this function's parameters.
local_key: SecioKeyPair,
rng: rand::SystemRandom,
// Locally-generated random number. The array size can be changed without any repercussion.
local_nonce: [u8; 16],
// Our local proposition's raw bytes.
local_public_key_in_protobuf_bytes: Vec<u8>,
local_proposition_bytes: Vec<u8>,
// The remote proposition's raw bytes.
remote_proposition_bytes: BytesMut,
remote_public_key_in_protobuf_bytes: Vec<u8>,
remote_public_key: Option<PublicKey>,
// The remote peer's version of `local_nonce`.
// If the NONCE size is actually part of the protocol, we can change this to a fixed-size
// array instead of a `Vec`.
remote_nonce: Vec<u8>,
// Set to `ordering(
// hash(concat(remote-pubkey, local-none)),
// hash(concat(local-pubkey, remote-none))
// )`.
// `Ordering::Equal` is an invalid value (as it would mean we're talking to ourselves).
//
// Since everything is symmetrical, this value is used to determine what should be ours
// and what should be the remote's.
hashes_ordering: Ordering,
// Crypto algorithms chosen for the communication.
chosen_exchange: Option<&'static agreement::Algorithm>,
// We only support AES for now, so store just a key size.
chosen_cipher: Option<KeySize>,
chosen_hash: Option<&'static digest::Algorithm>,
// Ephemeral key generated for the handshake and then thrown away.
local_tmp_priv_key: Option<EphemeralPrivateKey>,
local_tmp_pub_key: Vec<u8>,
}
let context = HandshakeContext {
local_key,
rng: rand::SystemRandom::new(),
local_nonce: Default::default(),
local_public_key_in_protobuf_bytes: Vec::new(),
local_proposition_bytes: Vec::new(),
remote_proposition_bytes: BytesMut::new(),
remote_public_key_in_protobuf_bytes: Vec::new(),
remote_public_key: None,
remote_nonce: Vec::new(),
hashes_ordering: Ordering::Equal,
chosen_exchange: None,
chosen_cipher: None,
chosen_hash: None,
local_tmp_priv_key: None,
local_tmp_pub_key: Vec::new(),
};
// The handshake messages all start with a 4-bytes message length prefix.
let socket = length_delimited::Builder::new()
.big_endian()
.length_field_length(4)
.new_framed(socket);
let future = future::ok::<_, SecioError>(context)
// Generate our nonce.
.and_then(|mut context| {
context.rng.fill(&mut context.local_nonce)
.map_err(|_| SecioError::NonceGenerationFailed)?;
trace!("starting handshake ; local nonce = {:?}", context.local_nonce);
Ok(context)
})
// Send our proposition with our nonce, public key and supported protocols.
.and_then(|mut context| {
context.local_public_key_in_protobuf_bytes = context.local_key.to_public_key().into_protobuf_encoding();
let mut proposition = Propose::new();
proposition.set_rand(context.local_nonce.to_vec());
proposition.set_pubkey(context.local_public_key_in_protobuf_bytes.clone());
proposition.set_exchanges(algo_support::exchanges::PROPOSITION_STRING.into());
proposition.set_ciphers(algo_support::ciphers::PROPOSITION_STRING.into());
proposition.set_hashes(algo_support::hashes::PROPOSITION_STRING.into());
let proposition_bytes = proposition.write_to_bytes().unwrap();
context.local_proposition_bytes = proposition_bytes.clone();
trace!("sending proposition to remote");
socket.send(BytesMut::from(proposition_bytes.clone()))
.from_err()
.map(|s| (s, context))
})
// Receive the remote's proposition.
.and_then(move |(socket, mut context)| {
socket.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(prop_raw, socket)| {
match prop_raw {
Some(p) => context.remote_proposition_bytes = p,
None => {
let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
debug!("unexpected eof while waiting for remote's proposition");
return Err(err.into())
},
};
let mut prop = match protobuf_parse_from_bytes::<Propose>(
&context.remote_proposition_bytes
) {
Ok(prop) => prop,
Err(_) => {
debug!("failed to parse remote's proposition protobuf message");
return Err(SecioError::HandshakeParsingFailure);
}
};
context.remote_public_key_in_protobuf_bytes = prop.take_pubkey();
let pubkey = match PublicKey::from_protobuf_encoding(&context.remote_public_key_in_protobuf_bytes) {
Ok(p) => p,
Err(_) => {
debug!("failed to parse remote's proposition's pubkey protobuf");
return Err(SecioError::HandshakeParsingFailure);
},
};
context.remote_nonce = prop.take_rand();
context.remote_public_key = Some(pubkey);
trace!("received proposition from remote ; pubkey = {:?} ; nonce = {:?}",
context.remote_public_key, context.remote_nonce);
Ok((prop, socket, context))
})
})
// Decide which algorithms to use (thanks to the remote's proposition).
.and_then(move |(remote_prop, socket, mut context)| {
// In order to determine which protocols to use, we compute two hashes and choose
// based on which hash is larger.
context.hashes_ordering = {
let oh1 = {
let mut ctx = digest::Context::new(&digest::SHA256);
ctx.update(&context.remote_public_key_in_protobuf_bytes);
ctx.update(&context.local_nonce);
ctx.finish()
};
let oh2 = {
let mut ctx = digest::Context::new(&digest::SHA256);
ctx.update(&context.local_public_key_in_protobuf_bytes);
ctx.update(&context.remote_nonce);
ctx.finish()
};
oh1.as_ref().cmp(&oh2.as_ref())
};
context.chosen_exchange = {
let list = &remote_prop.get_exchanges();
Some(match algo_support::exchanges::select_best(context.hashes_ordering, list) {
Ok(a) => a,
Err(err) => {
debug!("failed to select an exchange protocol");
return Err(err);
}
})
};
context.chosen_cipher = {
let list = &remote_prop.get_ciphers();
Some(match algo_support::ciphers::select_best(context.hashes_ordering, list) {
Ok(a) => a,
Err(err) => {
debug!("failed to select a cipher protocol");
return Err(err);
}
})
};
context.chosen_hash = {
let list = &remote_prop.get_hashes();
Some(match algo_support::hashes::select_best(context.hashes_ordering, list) {
Ok(a) => a,
Err(err) => {
debug!("failed to select a hash protocol");
return Err(err);
}
})
};
Ok((socket, context))
})
// Generate an ephemeral key for the negotiation.
.and_then(|(socket, context)| {
match EphemeralPrivateKey::generate(context.chosen_exchange.as_ref().unwrap(), &context.rng) {
Ok(tmp_priv_key) => Ok((socket, context, tmp_priv_key)),
Err(_) => {
debug!("failed to generate ECDH key");
Err(SecioError::EphemeralKeyGenerationFailed)
},
}
})
// Send the ephemeral pub key to the remote in an `Exchange` struct. The `Exchange` also
// contains a signature of the two propositions encoded with our static public key.
.and_then(|(socket, mut context, tmp_priv)| {
let exchange = {
let mut local_tmp_pub_key: Vec<u8> = (0 .. tmp_priv.public_key_len()).map(|_| 0).collect();
tmp_priv.compute_public_key(&mut local_tmp_pub_key).unwrap();
context.local_tmp_priv_key = Some(tmp_priv);
let mut data_to_sign = context.local_proposition_bytes.clone();
data_to_sign.extend_from_slice(&context.remote_proposition_bytes);
data_to_sign.extend_from_slice(&local_tmp_pub_key);
let mut exchange = Exchange::new();
exchange.set_epubkey(local_tmp_pub_key.clone());
exchange.set_signature({
match context.local_key.inner {
SecioKeyPairInner::Rsa { ref private, .. } => {
let mut state = match RSASigningState::new(private.clone()) {
Ok(s) => s,
Err(_) => {
debug!("failed to sign local exchange");
return Err(SecioError::SigningFailure);
},
};
let mut signature = vec![0; private.public_modulus_len()];
match state.sign(&RSA_PKCS1_SHA256, &context.rng, &data_to_sign,
&mut signature)
{
Ok(_) => (),
Err(_) => {
debug!("failed to sign local exchange");
return Err(SecioError::SigningFailure);
},
};
signature
},
SecioKeyPairInner::Ed25519 { ref key_pair } => {
let signature = key_pair.sign(&data_to_sign);
signature.as_ref().to_owned()
},
#[cfg(feature = "secp256k1")]
SecioKeyPairInner::Secp256k1 { ref private } => {
let data_to_sign = digest::digest(&digest::SHA256, &data_to_sign);
let message = secp256k1::Message::from_slice(data_to_sign.as_ref())
.expect("digest output length doesn't match secp256k1 input length");
let secp256k1 = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::SignOnly);
secp256k1
.sign(&message, private)
.expect("failed to sign message")
.serialize_der(&secp256k1)
},
}
});
exchange
};
let local_exch = exchange.write_to_bytes()
.expect("can only fail if the protobuf msg is malformed, which can't happen for \
this message in particular");
Ok((BytesMut::from(local_exch), socket, context))
})
// Send our local `Exchange`.
.and_then(|(local_exch, socket, context)| {
trace!("sending exchange to remote");
socket.send(local_exch)
.from_err()
.map(|s| (s, context))
})
// Receive the remote's `Exchange`.
.and_then(move |(socket, context)| {
socket.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(raw, socket)| {
let raw = match raw {
Some(r) => r,
None => {
let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
debug!("unexpected eof while waiting for remote's exchange");
return Err(err.into())
},
};
let remote_exch = match protobuf_parse_from_bytes::<Exchange>(&raw) {
Ok(e) => e,
Err(err) => {
debug!("failed to parse remote's exchange protobuf ; {:?}", err);
return Err(SecioError::HandshakeParsingFailure);
}
};
trace!("received and decoded the remote's exchange");
Ok((remote_exch, socket, context))
})
})
// Check the validity of the remote's `Exchange`. This verifies that the remote was really
// the sender of its proposition, and that it is the owner of both its global and ephemeral
// keys.
.and_then(|(remote_exch, socket, context)| {
let mut data_to_verify = context.remote_proposition_bytes.clone();
data_to_verify.extend_from_slice(&context.local_proposition_bytes);
data_to_verify.extend_from_slice(remote_exch.get_epubkey());
match context.remote_public_key {
Some(PublicKey::Rsa(ref remote_public_key)) => {
// TODO: The ring library doesn't like some stuff in our DER public key,
// therefore we scrap the first 24 bytes of the key. A proper fix would
// be to write a DER parser, but that's not trivial.
match signature_verify(&RSA_PKCS1_2048_8192_SHA256,
UntrustedInput::from(&remote_public_key[24..]),
UntrustedInput::from(&data_to_verify),
UntrustedInput::from(remote_exch.get_signature()))
{
Ok(()) => (),
Err(_) => {
debug!("failed to verify the remote's signature");
return Err(SecioError::SignatureVerificationFailed)
},
}
},
Some(PublicKey::Ed25519(ref remote_public_key)) => {
match signature_verify(&ED25519,
UntrustedInput::from(remote_public_key),
UntrustedInput::from(&data_to_verify),
UntrustedInput::from(remote_exch.get_signature()))
{
Ok(()) => (),
Err(_) => {
debug!("failed to verify the remote's signature");
return Err(SecioError::SignatureVerificationFailed)
},
}
},
#[cfg(feature = "secp256k1")]
Some(PublicKey::Secp256k1(ref remote_public_key)) => {
let data_to_verify = digest::digest(&digest::SHA256, &data_to_verify);
let message = secp256k1::Message::from_slice(data_to_verify.as_ref())
.expect("digest output length doesn't match secp256k1 input length");
let secp256k1 = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::VerifyOnly);
let signature = secp256k1::Signature::from_der(&secp256k1, remote_exch.get_signature());
let remote_public_key = secp256k1::key::PublicKey::from_slice(&secp256k1, remote_public_key);
if let (Ok(signature), Ok(remote_public_key)) = (signature, remote_public_key) {
match secp256k1.verify(&message, &signature, &remote_public_key) {
Ok(()) => (),
Err(_) => {
debug!("failed to verify the remote's signature");
return Err(SecioError::SignatureVerificationFailed)
},
}
} else {
debug!("remote's secp256k1 signature has wrong format");
return Err(SecioError::SignatureVerificationFailed)
}
},
#[cfg(not(feature = "secp256k1"))]
Some(PublicKey::Secp256k1(_)) => {
debug!("support for secp256k1 was disabled at compile-time");
return Err(SecioError::SignatureVerificationFailed);
},
None => unreachable!("we store a Some in the remote public key before reaching \
this point")
};
trace!("successfully verified the remote's signature");
Ok((remote_exch, socket, context))
})
// Generate a key from the local ephemeral private key and the remote ephemeral public key,
// derive from it a ciper key, an iv, and a hmac key, and build the encoder/decoder.
.and_then(|(remote_exch, socket, mut context)| {
let local_priv_key = context.local_tmp_priv_key.take()
.expect("we filled this Option earlier, and extract it now");
let codec = agreement::agree_ephemeral(local_priv_key,
&context.chosen_exchange.unwrap(),
UntrustedInput::from(remote_exch.get_epubkey()),
SecioError::SecretGenerationFailed,
|key_material| {
let key = SigningKey::new(context.chosen_hash.unwrap(), key_material);
let chosen_cipher = context.chosen_cipher.unwrap();
let (cipher_key_size, iv_size) = match chosen_cipher {
KeySize::KeySize128 => (16, 16),
KeySize::KeySize256 => (32, 16),
};
let mut longer_key = vec![0u8; 2 * (iv_size + cipher_key_size + 20)];
stretch_key(&key, &mut longer_key);
let (local_infos, remote_infos) = {
let (first_half, second_half) = longer_key.split_at(longer_key.len() / 2);
match context.hashes_ordering {
Ordering::Equal => panic!(),
Ordering::Less => (second_half, first_half),
Ordering::Greater => (first_half, second_half),
}
};
let (encoding_cipher, encoding_hmac) = {
let (iv, rest) = local_infos.split_at(iv_size);
let (cipher_key, mac_key) = rest.split_at(cipher_key_size);
let hmac = SigningKey::new(&context.chosen_hash.unwrap(), mac_key);
let cipher = ctr(chosen_cipher, cipher_key, iv);
(cipher, hmac)
};
let (decoding_cipher, decoding_hmac) = {
let (iv, rest) = remote_infos.split_at(iv_size);
let (cipher_key, mac_key) = rest.split_at(cipher_key_size);
let hmac = VerificationKey::new(&context.chosen_hash.unwrap(), mac_key);
let cipher = ctr(chosen_cipher, cipher_key, iv);
(cipher, hmac)
};
Ok(full_codec(socket, encoding_cipher, encoding_hmac,
decoding_cipher, decoding_hmac))
});
match codec {
Ok(c) => Ok((c, context)),
Err(err) => {
debug!("failed to generate shared secret with remote");
Err(err)
},
}
})
// We send back their nonce to check if the connection works.
.and_then(|(codec, mut context)| {
let remote_nonce = mem::replace(&mut context.remote_nonce, Vec::new());
trace!("checking encryption by sending back remote's nonce");
codec.send(BytesMut::from(remote_nonce))
.map(|s| (s, context))
.from_err()
})
// Check that the received nonce is correct.
.and_then(|(codec, context)| {
codec.into_future()
.map_err(|(e, _)| e)
.and_then(move |(nonce, rest)| {
match nonce {
Some(ref n) if n == &context.local_nonce => {
trace!("secio handshake success");
Ok((rest, context.remote_public_key.expect("we stored a Some earlier"), context.local_tmp_pub_key))
},
None => {
debug!("unexpected eof during nonce check");
Err(IoError::new(IoErrorKind::BrokenPipe, "unexpected eof").into())
},
_ => {
debug!("failed nonce verification with remote");
Err(SecioError::NonceVerificationFailed)
}
}
})
});
Box::new(future)
}
// Custom algorithm translated from reference implementations. Needs to be the same algorithm
// amongst all implementations.
fn stretch_key(key: &SigningKey, result: &mut [u8]) {
const SEED: &[u8] = b"key expansion";
let mut init_ctxt = SigningContext::with_key(key);
init_ctxt.update(SEED);
let mut a = init_ctxt.sign();
let mut j = 0;
while j < result.len() {
let mut context = SigningContext::with_key(key);
context.update(a.as_ref());
context.update(SEED);
let b = context.sign();
let todo = cmp::min(b.as_ref().len(), result.len() - j);
result[j..j + todo].copy_from_slice(&b.as_ref()[..todo]);
j += todo;
let mut context = SigningContext::with_key(key);
context.update(a.as_ref());
a = context.sign();
}
}
#[cfg(test)]
mod tests {
extern crate tokio_current_thread;
extern crate tokio_tcp;
use self::tokio_tcp::TcpListener;
use self::tokio_tcp::TcpStream;
use super::handshake;
use super::stretch_key;
use futures::Future;
use futures::Stream;
use ring::digest::SHA256;
use ring::hmac::SigningKey;
use SecioKeyPair;
#[test]
fn handshake_with_self_succeeds_rsa() {
let key1 = {
let private = include_bytes!("../tests/test-rsa-private-key.pk8");
let public = include_bytes!("../tests/test-rsa-public-key.der").to_vec();
SecioKeyPair::rsa_from_pkcs8(private, public).unwrap()
};
let key2 = {
let private = include_bytes!("../tests/test-rsa-private-key-2.pk8");
let public = include_bytes!("../tests/test-rsa-public-key-2.der").to_vec();
SecioKeyPair::rsa_from_pkcs8(private, public).unwrap()
};
handshake_with_self_succeeds(key1, key2);
}
#[test]
fn handshake_with_self_succeeds_ed25519() {
let key1 = SecioKeyPair::ed25519_generated().unwrap();
let key2 = SecioKeyPair::ed25519_generated().unwrap();
handshake_with_self_succeeds(key1, key2);
}
#[test]
#[cfg(feature = "secp256k1")]
fn handshake_with_self_succeeds_secp256k1() {
let key1 = {
let key = include_bytes!("../tests/test-secp256k1-private-key.der");
SecioKeyPair::secp256k1_from_der(&key[..]).unwrap()
};
let key2 = {
let key = include_bytes!("../tests/test-secp256k1-private-key-2.der");
SecioKeyPair::secp256k1_from_der(&key[..]).unwrap()
};
handshake_with_self_succeeds(key1, key2);
}
fn handshake_with_self_succeeds(key1: SecioKeyPair, key2: SecioKeyPair) {
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let listener_addr = listener.local_addr().unwrap();
let server = listener
.incoming()
.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(connec, _)| handshake(connec.unwrap(), key1));
let client = TcpStream::connect(&listener_addr)
.map_err(|e| e.into())
.and_then(move |stream| handshake(stream, key2));
tokio_current_thread::block_on_all(server.join(client)).unwrap();
}
#[test]
fn stretch() {
let mut output = [0u8; 32];
let key1 = SigningKey::new(&SHA256, &[]);
stretch_key(&key1, &mut output);
assert_eq!(
&output,
&[
103, 144, 60, 199, 85, 145, 239, 71, 79, 198, 85, 164, 32, 53, 143, 205, 50, 48,
153, 10, 37, 32, 85, 1, 226, 61, 193, 1, 154, 120, 207, 80,
]
);
let key2 = SigningKey::new(
&SHA256,
&[
157, 166, 80, 144, 77, 193, 198, 6, 23, 220, 87, 220, 191, 72, 168, 197, 54, 33,
219, 225, 84, 156, 165, 37, 149, 224, 244, 32, 170, 79, 125, 35, 171, 26, 178, 176,
92, 168, 22, 27, 205, 44, 229, 61, 152, 21, 222, 81, 241, 81, 116, 236, 74, 166,
89, 145, 5, 162, 108, 230, 55, 54, 9, 17,
],
);
stretch_key(&key2, &mut output);
assert_eq!(
&output,
&[
39, 151, 182, 63, 180, 175, 224, 139, 42, 131, 130, 116, 55, 146, 62, 31, 157, 95,
217, 15, 73, 81, 10, 83, 243, 141, 64, 227, 103, 144, 99, 121,
]
);
let key3 = SigningKey::new(
&SHA256,
&[
98, 219, 94, 104, 97, 70, 139, 13, 185, 110, 56, 36, 66, 3, 80, 224, 32, 205, 102,
170, 59, 32, 140, 245, 86, 102, 231, 68, 85, 249, 227, 243, 57, 53, 171, 36, 62,
225, 178, 74, 89, 142, 151, 94, 183, 231, 208, 166, 244, 130, 130, 209, 248, 65,
19, 48, 127, 127, 55, 82, 117, 154, 124, 108,
],
);
stretch_key(&key3, &mut output);
assert_eq!(
&output,
&[
28, 39, 158, 206, 164, 16, 211, 194, 99, 43, 208, 36, 24, 141, 90, 93, 157, 236,
238, 111, 170, 0, 60, 11, 49, 174, 177, 121, 30, 12, 182, 25,
]
);
}
}

394
protocols/secio/src/lib.rs Normal file
View File

@ -0,0 +1,394 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//! The `secio` protocol is a middleware that will encrypt and decrypt communications going
//! through a socket (or anything that implements `AsyncRead + AsyncWrite`).
//!
//! # Connection upgrade
//!
//! The `SecioConfig` struct implements the `ConnectionUpgrade` trait. You can apply it over a
//! `Transport` by using the `with_upgrade` method. The returned object will also implement
//! `Transport` and will automatically apply the secio protocol over any connection that is opened
//! through it.
//!
//! ```no_run
//! extern crate futures;
//! extern crate tokio_current_thread;
//! extern crate tokio_io;
//! extern crate libp2p_core;
//! extern crate libp2p_secio;
//! extern crate libp2p_tcp_transport;
//!
//! # fn main() {
//! use futures::Future;
//! use libp2p_secio::{SecioConfig, SecioKeyPair, SecioOutput};
//! use libp2p_core::{Multiaddr, Transport, upgrade};
//! use libp2p_tcp_transport::TcpConfig;
//! use tokio_io::io::write_all;
//!
//! let transport = TcpConfig::new()
//! .with_upgrade({
//! # let private_key = b"";
//! //let private_key = include_bytes!("test-rsa-private-key.pk8");
//! # let public_key = vec![];
//! //let public_key = include_bytes!("test-rsa-public-key.der").to_vec();
//! let upgrade = SecioConfig {
//! // See the documentation of `SecioKeyPair`.
//! key: SecioKeyPair::rsa_from_pkcs8(private_key, public_key).unwrap(),
//! };
//!
//! upgrade::map(upgrade, |out: SecioOutput<_>| out.stream)
//! });
//!
//! let future = transport.dial("/ip4/127.0.0.1/tcp/12345".parse::<Multiaddr>().unwrap())
//! .unwrap_or_else(|_| panic!("Unable to dial node"))
//! .and_then(|(connection, _)| {
//! // Sends "hello world" on the connection, will be encrypted.
//! write_all(connection, "hello world")
//! });
//!
//! tokio_current_thread::block_on_all(future).unwrap();
//! # }
//! ```
//!
//! # Manual usage
//!
//! > **Note**: You are encouraged to use `SecioConfig` as described above.
//!
//! You can add the `secio` layer over a socket by calling `SecioMiddleware::handshake()`. This
//! method will perform a handshake with the host, and return a future that corresponds to the
//! moment when the handshake succeeds or errored. On success, the future produces a
//! `SecioMiddleware` that implements `Sink` and `Stream` and can be used to send packets of data.
//!
extern crate aes_ctr;
#[cfg(feature = "secp256k1")]
extern crate asn1_der;
extern crate bytes;
extern crate futures;
extern crate libp2p_core;
#[macro_use]
extern crate log;
extern crate protobuf;
extern crate rand;
extern crate ring;
extern crate rw_stream_sink;
#[cfg(feature = "secp256k1")]
extern crate secp256k1;
extern crate tokio_io;
extern crate untrusted;
#[cfg(feature = "aes-all")]
#[macro_use]
extern crate lazy_static;
pub use self::error::SecioError;
#[cfg(feature = "secp256k1")]
use asn1_der::{traits::FromDerEncoded, traits::FromDerObject, DerObject};
use bytes::{Bytes, BytesMut};
use futures::stream::MapErr as StreamMapErr;
use futures::{Future, Poll, Sink, StartSend, Stream};
use libp2p_core::{PeerId, PublicKey};
use ring::rand::SystemRandom;
use ring::signature::{Ed25519KeyPair, 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;
mod error;
mod handshake;
mod structs_proto;
mod stream_cipher;
/// Implementation of the `ConnectionUpgrade` trait of `libp2p_core`. Automatically applies
/// secio on any connection.
#[derive(Clone)]
pub struct SecioConfig {
/// Private and public keys of the local node.
pub key: SecioKeyPair,
}
/// 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 {
/// Builds a `SecioKeyPair` from a PKCS8 private key and public key.
pub fn rsa_from_pkcs8<P>(
private: &[u8],
public: P,
) -> Result<SecioKeyPair, Box<Error + Send + Sync>>
where
P: Into<Vec<u8>>,
{
let private = RSAKeyPair::from_pkcs8(Input::from(&private[..])).map_err(Box::new)?;
Ok(SecioKeyPair {
inner: SecioKeyPairInner::Rsa {
public: public.into(),
private: Arc::new(private),
},
})
}
/// Builds a `SecioKeyPair` from a PKCS8 ED25519 private key.
pub fn ed25519_from_pkcs8<K>(key: K) -> Result<SecioKeyPair, Box<Error + Send + Sync>>
where
K: AsRef<[u8]>,
{
let key_pair = Ed25519KeyPair::from_pkcs8(Input::from(key.as_ref())).map_err(Box::new)?;
Ok(SecioKeyPair {
inner: SecioKeyPairInner::Ed25519 {
key_pair: Arc::new(key_pair),
},
})
}
/// Generates a new Ed25519 key pair and uses it.
pub fn ed25519_generated() -> Result<SecioKeyPair, Box<Error + Send + Sync>> {
let rng = SystemRandom::new();
let gen = Ed25519KeyPair::generate_pkcs8(&rng).map_err(Box::new)?;
Ok(SecioKeyPair::ed25519_from_pkcs8(&gen[..])
.expect("failed to parse generated Ed25519 key"))
}
/// Builds a `SecioKeyPair` from a raw secp256k1 32 bytes private key.
#[cfg(feature = "secp256k1")]
pub fn secp256k1_raw_key<K>(key: K) -> Result<SecioKeyPair, Box<Error + Send + Sync>>
where
K: AsRef<[u8]>,
{
let secp = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::None);
let private = secp256k1::key::SecretKey::from_slice(&secp, key.as_ref())?;
Ok(SecioKeyPair {
inner: SecioKeyPairInner::Secp256k1 { private },
})
}
/// Builds a `SecioKeyPair` from a secp256k1 private key in DER format.
#[cfg(feature = "secp256k1")]
pub fn secp256k1_from_der<K>(key: K) -> Result<SecioKeyPair, Box<Error + Send + Sync>>
where
K: AsRef<[u8]>,
{
// See ECPrivateKey in https://tools.ietf.org/html/rfc5915
let obj: Vec<DerObject> =
FromDerEncoded::with_der_encoded(key.as_ref()).map_err(|err| err.to_string())?;
let priv_key_obj = obj.into_iter()
.nth(1)
.ok_or_else(|| "Not enough elements in DER".to_string())?;
let private_key: Vec<u8> =
FromDerObject::from_der_object(priv_key_obj).map_err(|err| err.to_string())?;
SecioKeyPair::secp256k1_raw_key(&private_key)
}
/// Returns the public key corresponding to this key pair.
pub fn to_public_key(&self) -> PublicKey {
match self.inner {
SecioKeyPairInner::Rsa { ref public, .. } => PublicKey::Rsa(public.clone()),
SecioKeyPairInner::Ed25519 { ref key_pair } => {
PublicKey::Ed25519(key_pair.public_key_bytes().to_vec())
}
#[cfg(feature = "secp256k1")]
SecioKeyPairInner::Secp256k1 { ref private } => {
let secp = secp256k1::Secp256k1::with_caps(secp256k1::ContextFlag::SignOnly);
let pubkey = secp256k1::key::PublicKey::from_secret_key(&secp, private)
.expect("wrong secp256k1 private key ; type safety violated");
PublicKey::Secp256k1(pubkey.serialize_vec(&secp, true).to_vec())
}
}
}
/// Builds a `PeerId` corresponding to the public key of this key pair.
#[inline]
pub fn to_peer_id(&self) -> PeerId {
self.to_public_key().into_peer_id()
}
// TODO: method to save generated key on disk?
}
// Inner content of `SecioKeyPair`.
#[derive(Clone)]
enum SecioKeyPairInner {
Rsa {
public: Vec<u8>,
// We use an `Arc` so that we can clone the enum.
private: Arc<RSAKeyPair>,
},
Ed25519 {
// We use an `Arc` so that we can clone the enum.
key_pair: Arc<Ed25519KeyPair>,
},
#[cfg(feature = "secp256k1")]
Secp256k1 { private: secp256k1::key::SecretKey },
}
/// Output of the secio protocol.
pub struct SecioOutput<S>
where
S: AsyncRead + AsyncWrite,
{
/// The encrypted stream.
pub stream: RwStreamSink<StreamMapErr<SecioMiddleware<S>, fn(SecioError) -> IoError>>,
/// The public key of the remote.
pub remote_key: PublicKey,
/// Ephemeral public key used during the negotiation.
pub ephemeral_public_key: Vec<u8>,
}
impl<S, Maf> libp2p_core::ConnectionUpgrade<S, Maf> for SecioConfig
where
S: AsyncRead + AsyncWrite + 'static, // TODO: 'static :(
Maf: 'static, // TODO: 'static :(
{
type Output = SecioOutput<S>;
type MultiaddrFuture = Maf;
type Future = Box<Future<Item = (Self::Output, Maf), Error = IoError>>;
type NamesIter = iter::Once<(Bytes, ())>;
type UpgradeIdentifier = ();
#[inline]
fn protocol_names(&self) -> Self::NamesIter {
iter::once(("/secio/1.0.0".into(), ()))
}
#[inline]
fn upgrade(
self,
incoming: S,
_: (),
_: libp2p_core::Endpoint,
remote_addr: Maf,
) -> Self::Future {
debug!("Starting secio upgrade");
let fut = SecioMiddleware::handshake(incoming, self.key);
let wrapped = fut.map(|(stream_sink, pubkey, ephemeral)| {
let mapped = stream_sink.map_err(map_err as fn(_) -> _);
SecioOutput {
stream: RwStreamSink::new(mapped),
remote_key: pubkey,
ephemeral_public_key: ephemeral,
}
}).map_err(map_err);
Box::new(wrapped.map(move |out| (out, remote_addr)))
}
}
#[inline]
fn map_err(err: SecioError) -> IoError {
debug!("error during secio handshake {:?}", err);
IoError::new(IoErrorKind::InvalidData, err)
}
/// Wraps around an object that implements `AsyncRead` and `AsyncWrite`.
///
/// Implements `Sink` and `Stream` whose items are frames of data. Each frame is encoded
/// individually, so you are encouraged to group data in few frames if possible.
pub struct SecioMiddleware<S> {
inner: codec::FullCodec<S>,
}
impl<S> SecioMiddleware<S>
where
S: AsyncRead + AsyncWrite,
{
/// Attempts to perform a handshake on the given socket.
///
/// On success, produces a `SecioMiddleware` that can then be used to encode/decode
/// communications, plus the public key of the remote, plus the ephemeral public key.
pub fn handshake<'a>(
socket: S,
key_pair: SecioKeyPair,
) -> Box<Future<Item = (SecioMiddleware<S>, PublicKey, Vec<u8>), Error = SecioError> + 'a>
where
S: 'a,
{
let fut = handshake::handshake(socket, key_pair).map(|(inner, pubkey, ephemeral)| {
let inner = SecioMiddleware { inner };
(inner, pubkey, ephemeral)
});
Box::new(fut)
}
}
impl<S> Sink for SecioMiddleware<S>
where
S: AsyncRead + AsyncWrite,
{
type SinkItem = BytesMut;
type SinkError = IoError;
#[inline]
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
self.inner.start_send(item)
}
#[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
self.inner.poll_complete()
}
}
impl<S> Stream for SecioMiddleware<S>
where
S: AsyncRead + AsyncWrite,
{
type Item = Vec<u8>;
type Error = SecioError;
#[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.inner.poll()
}
}

View File

@ -0,0 +1,135 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use super::codec::StreamCipher;
use aes_ctr::stream_cipher::generic_array::GenericArray;
use aes_ctr::stream_cipher::NewFixStreamCipher;
use aes_ctr::{Aes128Ctr, Aes256Ctr};
#[derive(Clone, Copy)]
pub enum KeySize {
KeySize128,
KeySize256,
}
/// Returns your stream cipher depending on `KeySize`.
#[cfg(not(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86"))))]
pub fn ctr(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher {
ctr_int(key_size, key, iv)
}
/// Returns your stream cipher depending on `KeySize`.
#[cfg(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86")))]
pub fn ctr(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher {
if *aes_alt::AES_NI {
aes_alt::ctr_alt(key_size, key, iv)
} else {
ctr_int(key_size, key, iv)
}
}
#[cfg(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86")))]
mod aes_alt {
extern crate ctr;
extern crate aesni;
use ::codec::StreamCipher;
use self::ctr::Ctr128;
use self::aesni::{Aes128, Aes256};
use self::ctr::stream_cipher::NewFixStreamCipher;
use self::ctr::stream_cipher::generic_array::GenericArray;
use super::KeySize;
lazy_static! {
pub static ref AES_NI: bool = is_x86_feature_detected!("aes")
&& is_x86_feature_detected!("sse2")
&& is_x86_feature_detected!("sse3");
}
/// AES-128 in CTR mode
pub type Aes128Ctr = Ctr128<Aes128>;
/// AES-256 in CTR mode
pub type Aes256Ctr = Ctr128<Aes256>;
/// Returns alternate stream cipher if target functionalities does not allow standard one.
/// Eg : aes without sse
pub fn ctr_alt(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher {
match key_size {
KeySize::KeySize128 => Box::new(Aes128Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
KeySize::KeySize256 => Box::new(Aes256Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
}
}
}
#[inline]
fn ctr_int(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher {
match key_size {
KeySize::KeySize128 => Box::new(Aes128Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
KeySize::KeySize256 => Box::new(Aes256Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
}
}
#[cfg(all(
feature = "aes-all",
any(target_arch = "x86_64", target_arch = "x86"),
))]
#[cfg(test)]
mod tests {
use super::{KeySize, ctr};
#[test]
fn assert_non_native_run() {
// this test is for asserting aes unsuported opcode does not break on old cpu
let key = [0;16];
let iv = [0;16];
let mut aes = ctr(KeySize::KeySize128, &key, &iv);
let mut content = [0;16];
assert!(aes
.try_apply_keystream(&mut content).is_ok());
}
}
// aesni compile check for aes-all (aes-all import aesni through aes_ctr only if those checks pass)
#[cfg(all(
feature = "aes-all",
any(target_arch = "x86_64", target_arch = "x86"),
any(target_feature = "aes", target_feature = "ssse3"),
))]
compile_error!(
"aes-all must be compile without aes and sse3 flags : currently \
is_x86_feature_detected macro will not detect feature correctly otherwhise. \
RUSTFLAGS=\"-C target-feature=+aes,+ssse3\" enviromental variable. \
For x86 target arch additionally enable sse2 target feature."
);

View File

@ -0,0 +1,681 @@
// This file is generated by rust-protobuf 2.0.2. Do not edit
// @generated
// https://github.com/Manishearth/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy)]
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unsafe_code)]
#![allow(unused_imports)]
#![allow(unused_results)]
use protobuf::Message as Message_imported_for_functions;
use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions;
#[derive(PartialEq,Clone,Default)]
pub struct Propose {
// message fields
rand: ::protobuf::SingularField<::std::vec::Vec<u8>>,
pubkey: ::protobuf::SingularField<::std::vec::Vec<u8>>,
exchanges: ::protobuf::SingularField<::std::string::String>,
ciphers: ::protobuf::SingularField<::std::string::String>,
hashes: ::protobuf::SingularField<::std::string::String>,
// special fields
unknown_fields: ::protobuf::UnknownFields,
cached_size: ::protobuf::CachedSize,
}
impl Propose {
pub fn new() -> Propose {
::std::default::Default::default()
}
// optional bytes rand = 1;
pub fn clear_rand(&mut self) {
self.rand.clear();
}
pub fn has_rand(&self) -> bool {
self.rand.is_some()
}
// Param is passed by value, moved
pub fn set_rand(&mut self, v: ::std::vec::Vec<u8>) {
self.rand = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_rand(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.rand.is_none() {
self.rand.set_default();
}
self.rand.as_mut().unwrap()
}
// Take field
pub fn take_rand(&mut self) -> ::std::vec::Vec<u8> {
self.rand.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub fn get_rand(&self) -> &[u8] {
match self.rand.as_ref() {
Some(v) => &v,
None => &[],
}
}
// optional bytes pubkey = 2;
pub fn clear_pubkey(&mut self) {
self.pubkey.clear();
}
pub fn has_pubkey(&self) -> bool {
self.pubkey.is_some()
}
// Param is passed by value, moved
pub fn set_pubkey(&mut self, v: ::std::vec::Vec<u8>) {
self.pubkey = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_pubkey(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.pubkey.is_none() {
self.pubkey.set_default();
}
self.pubkey.as_mut().unwrap()
}
// Take field
pub fn take_pubkey(&mut self) -> ::std::vec::Vec<u8> {
self.pubkey.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub fn get_pubkey(&self) -> &[u8] {
match self.pubkey.as_ref() {
Some(v) => &v,
None => &[],
}
}
// optional string exchanges = 3;
pub fn clear_exchanges(&mut self) {
self.exchanges.clear();
}
pub fn has_exchanges(&self) -> bool {
self.exchanges.is_some()
}
// Param is passed by value, moved
pub fn set_exchanges(&mut self, v: ::std::string::String) {
self.exchanges = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_exchanges(&mut self) -> &mut ::std::string::String {
if self.exchanges.is_none() {
self.exchanges.set_default();
}
self.exchanges.as_mut().unwrap()
}
// Take field
pub fn take_exchanges(&mut self) -> ::std::string::String {
self.exchanges.take().unwrap_or_else(|| ::std::string::String::new())
}
pub fn get_exchanges(&self) -> &str {
match self.exchanges.as_ref() {
Some(v) => &v,
None => "",
}
}
// optional string ciphers = 4;
pub fn clear_ciphers(&mut self) {
self.ciphers.clear();
}
pub fn has_ciphers(&self) -> bool {
self.ciphers.is_some()
}
// Param is passed by value, moved
pub fn set_ciphers(&mut self, v: ::std::string::String) {
self.ciphers = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_ciphers(&mut self) -> &mut ::std::string::String {
if self.ciphers.is_none() {
self.ciphers.set_default();
}
self.ciphers.as_mut().unwrap()
}
// Take field
pub fn take_ciphers(&mut self) -> ::std::string::String {
self.ciphers.take().unwrap_or_else(|| ::std::string::String::new())
}
pub fn get_ciphers(&self) -> &str {
match self.ciphers.as_ref() {
Some(v) => &v,
None => "",
}
}
// optional string hashes = 5;
pub fn clear_hashes(&mut self) {
self.hashes.clear();
}
pub fn has_hashes(&self) -> bool {
self.hashes.is_some()
}
// Param is passed by value, moved
pub fn set_hashes(&mut self, v: ::std::string::String) {
self.hashes = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_hashes(&mut self) -> &mut ::std::string::String {
if self.hashes.is_none() {
self.hashes.set_default();
}
self.hashes.as_mut().unwrap()
}
// Take field
pub fn take_hashes(&mut self) -> ::std::string::String {
self.hashes.take().unwrap_or_else(|| ::std::string::String::new())
}
pub fn get_hashes(&self) -> &str {
match self.hashes.as_ref() {
Some(v) => &v,
None => "",
}
}
}
impl ::protobuf::Message for Propose {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_bytes_into(wire_type, is, &mut self.rand)?;
},
2 => {
::protobuf::rt::read_singular_bytes_into(wire_type, is, &mut self.pubkey)?;
},
3 => {
::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.exchanges)?;
},
4 => {
::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.ciphers)?;
},
5 => {
::protobuf::rt::read_singular_string_into(wire_type, is, &mut self.hashes)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if let Some(ref v) = self.rand.as_ref() {
my_size += ::protobuf::rt::bytes_size(1, &v);
}
if let Some(ref v) = self.pubkey.as_ref() {
my_size += ::protobuf::rt::bytes_size(2, &v);
}
if let Some(ref v) = self.exchanges.as_ref() {
my_size += ::protobuf::rt::string_size(3, &v);
}
if let Some(ref v) = self.ciphers.as_ref() {
my_size += ::protobuf::rt::string_size(4, &v);
}
if let Some(ref v) = self.hashes.as_ref() {
my_size += ::protobuf::rt::string_size(5, &v);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
if let Some(ref v) = self.rand.as_ref() {
os.write_bytes(1, &v)?;
}
if let Some(ref v) = self.pubkey.as_ref() {
os.write_bytes(2, &v)?;
}
if let Some(ref v) = self.exchanges.as_ref() {
os.write_string(3, &v)?;
}
if let Some(ref v) = self.ciphers.as_ref() {
os.write_string(4, &v)?;
}
if let Some(ref v) = self.hashes.as_ref() {
os.write_string(5, &v)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn as_any_mut(&mut self) -> &mut ::std::any::Any {
self as &mut ::std::any::Any
}
fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> Propose {
Propose::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"rand",
|m: &Propose| { &m.rand },
|m: &mut Propose| { &mut m.rand },
));
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"pubkey",
|m: &Propose| { &m.pubkey },
|m: &mut Propose| { &mut m.pubkey },
));
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"exchanges",
|m: &Propose| { &m.exchanges },
|m: &mut Propose| { &mut m.exchanges },
));
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"ciphers",
|m: &Propose| { &m.ciphers },
|m: &mut Propose| { &mut m.ciphers },
));
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"hashes",
|m: &Propose| { &m.hashes },
|m: &mut Propose| { &mut m.hashes },
));
::protobuf::reflect::MessageDescriptor::new::<Propose>(
"Propose",
fields,
file_descriptor_proto()
)
})
}
}
fn default_instance() -> &'static Propose {
static mut instance: ::protobuf::lazy::Lazy<Propose> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const Propose,
};
unsafe {
instance.get(Propose::new)
}
}
}
impl ::protobuf::Clear for Propose {
fn clear(&mut self) {
self.clear_rand();
self.clear_pubkey();
self.clear_exchanges();
self.clear_ciphers();
self.clear_hashes();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for Propose {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for Propose {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct Exchange {
// message fields
epubkey: ::protobuf::SingularField<::std::vec::Vec<u8>>,
signature: ::protobuf::SingularField<::std::vec::Vec<u8>>,
// special fields
unknown_fields: ::protobuf::UnknownFields,
cached_size: ::protobuf::CachedSize,
}
impl Exchange {
pub fn new() -> Exchange {
::std::default::Default::default()
}
// optional bytes epubkey = 1;
pub fn clear_epubkey(&mut self) {
self.epubkey.clear();
}
pub fn has_epubkey(&self) -> bool {
self.epubkey.is_some()
}
// Param is passed by value, moved
pub fn set_epubkey(&mut self, v: ::std::vec::Vec<u8>) {
self.epubkey = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_epubkey(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.epubkey.is_none() {
self.epubkey.set_default();
}
self.epubkey.as_mut().unwrap()
}
// Take field
pub fn take_epubkey(&mut self) -> ::std::vec::Vec<u8> {
self.epubkey.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub fn get_epubkey(&self) -> &[u8] {
match self.epubkey.as_ref() {
Some(v) => &v,
None => &[],
}
}
// optional bytes signature = 2;
pub fn clear_signature(&mut self) {
self.signature.clear();
}
pub fn has_signature(&self) -> bool {
self.signature.is_some()
}
// Param is passed by value, moved
pub fn set_signature(&mut self, v: ::std::vec::Vec<u8>) {
self.signature = ::protobuf::SingularField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_signature(&mut self) -> &mut ::std::vec::Vec<u8> {
if self.signature.is_none() {
self.signature.set_default();
}
self.signature.as_mut().unwrap()
}
// Take field
pub fn take_signature(&mut self) -> ::std::vec::Vec<u8> {
self.signature.take().unwrap_or_else(|| ::std::vec::Vec::new())
}
pub fn get_signature(&self) -> &[u8] {
match self.signature.as_ref() {
Some(v) => &v,
None => &[],
}
}
}
impl ::protobuf::Message for Exchange {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_bytes_into(wire_type, is, &mut self.epubkey)?;
},
2 => {
::protobuf::rt::read_singular_bytes_into(wire_type, is, &mut self.signature)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if let Some(ref v) = self.epubkey.as_ref() {
my_size += ::protobuf::rt::bytes_size(1, &v);
}
if let Some(ref v) = self.signature.as_ref() {
my_size += ::protobuf::rt::bytes_size(2, &v);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream) -> ::protobuf::ProtobufResult<()> {
if let Some(ref v) = self.epubkey.as_ref() {
os.write_bytes(1, &v)?;
}
if let Some(ref v) = self.signature.as_ref() {
os.write_bytes(2, &v)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &::std::any::Any {
self as &::std::any::Any
}
fn as_any_mut(&mut self) -> &mut ::std::any::Any {
self as &mut ::std::any::Any
}
fn into_any(self: Box<Self>) -> ::std::boxed::Box<::std::any::Any> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> Exchange {
Exchange::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::reflect::MessageDescriptor,
};
unsafe {
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"epubkey",
|m: &Exchange| { &m.epubkey },
|m: &mut Exchange| { &mut m.epubkey },
));
fields.push(::protobuf::reflect::accessor::make_singular_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
"signature",
|m: &Exchange| { &m.signature },
|m: &mut Exchange| { &mut m.signature },
));
::protobuf::reflect::MessageDescriptor::new::<Exchange>(
"Exchange",
fields,
file_descriptor_proto()
)
})
}
}
fn default_instance() -> &'static Exchange {
static mut instance: ::protobuf::lazy::Lazy<Exchange> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const Exchange,
};
unsafe {
instance.get(Exchange::new)
}
}
}
impl ::protobuf::Clear for Exchange {
fn clear(&mut self) {
self.clear_epubkey();
self.clear_signature();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for Exchange {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for Exchange {
fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef {
::protobuf::reflect::ProtobufValueRef::Message(self)
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\rstructs.proto\x12\x08spipe.pb\"\x85\x01\n\x07Propose\x12\x12\n\x04ra\
nd\x18\x01\x20\x01(\x0cR\x04rand\x12\x16\n\x06pubkey\x18\x02\x20\x01(\
\x0cR\x06pubkey\x12\x1c\n\texchanges\x18\x03\x20\x01(\tR\texchanges\x12\
\x18\n\x07ciphers\x18\x04\x20\x01(\tR\x07ciphers\x12\x16\n\x06hashes\x18\
\x05\x20\x01(\tR\x06hashes\"B\n\x08Exchange\x12\x18\n\x07epubkey\x18\x01\
\x20\x01(\x0cR\x07epubkey\x12\x1c\n\tsignature\x18\x02\x20\x01(\x0cR\tsi\
gnatureJ\xa5\x04\n\x06\x12\x04\0\0\r\x01\n\x08\n\x01\x02\x12\x03\0\x08\
\x10\n\n\n\x02\x04\0\x12\x04\x02\0\x08\x01\n\n\n\x03\x04\0\x01\x12\x03\
\x02\x08\x0f\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x08\x20\n\x0c\n\x05\x04\
\0\x02\0\x04\x12\x03\x03\x08\x10\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\
\x11\x16\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x17\x1b\n\x0c\n\x05\x04\
\0\x02\0\x03\x12\x03\x03\x1e\x1f\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\
\x08\"\n\x0c\n\x05\x04\0\x02\x01\x04\x12\x03\x04\x08\x10\n\x0c\n\x05\x04\
\0\x02\x01\x05\x12\x03\x04\x11\x16\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\
\x04\x17\x1d\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x20!\n\x0b\n\x04\
\x04\0\x02\x02\x12\x03\x05\x08&\n\x0c\n\x05\x04\0\x02\x02\x04\x12\x03\
\x05\x08\x10\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x11\x17\n\x0c\n\
\x05\x04\0\x02\x02\x01\x12\x03\x05\x18!\n\x0c\n\x05\x04\0\x02\x02\x03\
\x12\x03\x05$%\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x08$\n\x0c\n\x05\
\x04\0\x02\x03\x04\x12\x03\x06\x08\x10\n\x0c\n\x05\x04\0\x02\x03\x05\x12\
\x03\x06\x11\x17\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06\x18\x1f\n\x0c\
\n\x05\x04\0\x02\x03\x03\x12\x03\x06\"#\n\x0b\n\x04\x04\0\x02\x04\x12\
\x03\x07\x08#\n\x0c\n\x05\x04\0\x02\x04\x04\x12\x03\x07\x08\x10\n\x0c\n\
\x05\x04\0\x02\x04\x05\x12\x03\x07\x11\x17\n\x0c\n\x05\x04\0\x02\x04\x01\
\x12\x03\x07\x18\x1e\n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x07!\"\n\n\n\
\x02\x04\x01\x12\x04\n\0\r\x01\n\n\n\x03\x04\x01\x01\x12\x03\n\x08\x10\n\
\x0b\n\x04\x04\x01\x02\0\x12\x03\x0b\x08#\n\x0c\n\x05\x04\x01\x02\0\x04\
\x12\x03\x0b\x08\x10\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x0b\x11\x16\n\
\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x0b\x17\x1e\n\x0c\n\x05\x04\x01\x02\
\0\x03\x12\x03\x0b!\"\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x0c\x08%\n\x0c\
\n\x05\x04\x01\x02\x01\x04\x12\x03\x0c\x08\x10\n\x0c\n\x05\x04\x01\x02\
\x01\x05\x12\x03\x0c\x11\x16\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x0c\
\x17\x20\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x0c#$\
";
static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy {
lock: ::protobuf::lazy::ONCE_INIT,
ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto,
};
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
unsafe {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}
}