Revert "Revert "remove rustc-serialize (#359) (#386)" (#466)" (#477)

This reverts commit 0a3d4cdfad.
This commit is contained in:
Pierre Krieger
2018-09-17 10:05:37 +02:00
committed by GitHub
parent 2a7a48b496
commit d719ac6037
8 changed files with 181 additions and 58 deletions

View File

@ -13,7 +13,11 @@ log = "0.4.1"
protobuf = "2.0.2" protobuf = "2.0.2"
rand = "0.3.17" rand = "0.3.17"
ring = { version = "0.12", features = ["rsa_signing"] } ring = { version = "0.12", features = ["rsa_signing"] }
rust-crypto = "^0.2" aes-ctr = "0.1.0"
aesni = { version = "0.4.1", features = ["nocheck"], optional = true }
twofish = "0.1.0"
ctr = "0.1"
lazy_static = { version = "0.2.11", optional = true }
rw-stream-sink = { path = "../../misc/rw-stream-sink" } rw-stream-sink = { path = "../../misc/rw-stream-sink" }
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", optional = true } eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", optional = true }
tokio-io = "0.1.0" tokio-io = "0.1.0"
@ -22,6 +26,7 @@ untrusted = "0.5"
[features] [features]
default = ["secp256k1"] default = ["secp256k1"]
secp256k1 = ["eth-secp256k1"] secp256k1 = ["eth-secp256k1"]
aes-all = ["aesni","lazy_static"]
[dev-dependencies] [dev-dependencies]
libp2p-tcp-transport = { path = "../../transports/tcp" } libp2p-tcp-transport = { path = "../../transports/tcp" }

View File

@ -33,13 +33,14 @@ const ECDH_P384: &str = "P-384";
const AES_128: &str = "AES-128"; const AES_128: &str = "AES-128";
const AES_256: &str = "AES-256"; const AES_256: &str = "AES-256";
const TWOFISH_CTR: &str = "TwofishCTR";
const NULL: &str = "NULL"; const NULL: &str = "NULL";
const SHA_256: &str = "SHA256"; const SHA_256: &str = "SHA256";
const SHA_512: &str = "SHA512"; const SHA_512: &str = "SHA512";
pub(crate) const DEFAULT_AGREEMENTS_PROPOSITION: &str = "P-256,P-384"; pub(crate) const DEFAULT_AGREEMENTS_PROPOSITION: &str = "P-256,P-384";
pub(crate) const DEFAULT_CIPHERS_PROPOSITION: &str = "AES-128,AES-256"; pub(crate) const DEFAULT_CIPHERS_PROPOSITION: &str = "AES-128,AES-256,TwofishCTR";
pub(crate) const DEFAULT_DIGESTS_PROPOSITION: &str = "SHA256,SHA512"; pub(crate) const DEFAULT_DIGESTS_PROPOSITION: &str = "SHA256,SHA512";
@ -110,6 +111,10 @@ where
s.push_str(AES_256); s.push_str(AES_256);
s.push(',') s.push(',')
} }
Cipher::TwofishCtr => {
s.push_str(TWOFISH_CTR);
s.push(',')
}
Cipher::Null => { Cipher::Null => {
s.push_str(NULL); s.push_str(NULL);
s.push(',') s.push(',')
@ -134,6 +139,7 @@ pub fn select_cipher(r: Ordering, ours: &str, theirs: &str) -> Result<Cipher, Se
match x { match x {
AES_128 => return Ok(Cipher::Aes128), AES_128 => return Ok(Cipher::Aes128),
AES_256 => return Ok(Cipher::Aes256), AES_256 => return Ok(Cipher::Aes256),
TWOFISH_CTR => return Ok(Cipher::TwofishCtr),
NULL => return Ok(Cipher::Null), NULL => return Ok(Cipher::Null),
_ => continue _ => continue
} }

View File

@ -21,7 +21,8 @@
//! Individual messages decoding. //! Individual messages decoding.
use bytes::BytesMut; use bytes::BytesMut;
use codec::StreamCipher; use super::StreamCipher;
use error::SecioError; use error::SecioError;
use futures::sink::Sink; use futures::sink::Sink;
use futures::stream::Stream; use futures::stream::Stream;
@ -87,21 +88,24 @@ where
debug!("frame too short when decoding secio frame"); debug!("frame too short when decoding secio frame");
return Err(SecioError::FrameTooShort); return Err(SecioError::FrameTooShort);
} }
let content_length = frame.len() - hmac_num_bytes;
let (crypted_data, expected_hash) = frame.split_at(frame.len() - hmac_num_bytes); {
let (crypted_data, expected_hash) = frame.split_at(content_length);
debug_assert_eq!(expected_hash.len(), hmac_num_bytes); debug_assert_eq!(expected_hash.len(), hmac_num_bytes);
if hmac::verify(&self.hmac_key, crypted_data, expected_hash).is_err() { if hmac::verify(&self.hmac_key, crypted_data, expected_hash).is_err() {
debug!("hmac mismatch when decoding secio frame"); debug!("hmac mismatch when decoding secio frame");
return Err(SecioError::HmacNotMatching); return Err(SecioError::HmacNotMatching);
} }
}
// Note that there is no way to decipher in place with rust-crypto right now. let mut data_buf = frame.to_vec();
let mut decrypted_data = crypted_data.to_vec(); data_buf.truncate(content_length);
self.cipher_state self.cipher_state
.process(&crypted_data, &mut decrypted_data); .try_apply_keystream(&mut data_buf)
.map_err::<SecioError,_>(|e|e.into())?;
Ok(Async::Ready(Some(decrypted_data))) Ok(Async::Ready(Some(data_buf)))
} }
} }

View File

@ -21,7 +21,7 @@
//! Individual messages encoding. //! Individual messages encoding.
use bytes::BytesMut; use bytes::BytesMut;
use codec::StreamCipher; use super::StreamCipher;
use futures::sink::Sink; use futures::sink::Sink;
use futures::stream::Stream; use futures::stream::Stream;
use futures::Poll; use futures::Poll;
@ -62,22 +62,15 @@ where
type SinkItem = BytesMut; type SinkItem = BytesMut;
type SinkError = S::SinkError; type SinkError = S::SinkError;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(&mut self, mut data_buf: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
let capacity = item.len() + self.hmac_key.digest_algorithm().output_len;
// Apparently this is the fastest way of doing. // TODO if SinkError gets refactor to SecioError,
// See https://gist.github.com/kirushik/e0d93759b0cd102f814408595c20a9d0 // then use try_apply_keystream
let mut out_buffer = BytesMut::from(vec![0; capacity]); 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)
{
let (out_data, out_sign) = out_buffer.split_at_mut(item.len());
self.cipher_state.process(&item, out_data);
let signature = hmac::sign(&self.hmac_key, out_data);
out_sign.copy_from_slice(signature.as_ref());
}
self.raw_sink.start_send(out_buffer)
} }
#[inline] #[inline]

View File

@ -24,7 +24,7 @@
use self::decode::DecoderMiddleware; use self::decode::DecoderMiddleware;
use self::encode::EncoderMiddleware; use self::encode::EncoderMiddleware;
use crypto::symmetriccipher::SynchronousStreamCipher; use aes_ctr::stream_cipher::StreamCipherCore;
use ring::hmac; use ring::hmac;
use tokio_io::codec::length_delimited; use tokio_io::codec::length_delimited;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
@ -35,7 +35,7 @@ mod encode;
/// Type returned by `full_codec`. /// Type returned by `full_codec`.
pub type FullCodec<S> = DecoderMiddleware<EncoderMiddleware<length_delimited::Framed<S>>>; pub type FullCodec<S> = DecoderMiddleware<EncoderMiddleware<length_delimited::Framed<S>>>;
pub type StreamCipher = Box<dyn SynchronousStreamCipher + Send>; pub type StreamCipher = Box<dyn StreamCipherCore + Send>;
/// Takes control of `socket`. Returns an object that implements `future::Sink` and /// Takes control of `socket`. Returns an object that implements `future::Sink` and
@ -90,6 +90,7 @@ mod tests {
let cipher_key: [u8; 32] = rand::random(); let cipher_key: [u8; 32] = rand::random();
let hmac_key: [u8; 32] = rand::random(); let hmac_key: [u8; 32] = rand::random();
let encoder = EncoderMiddleware::new( let encoder = EncoderMiddleware::new(
data_tx, data_tx,
ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]), ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]),
@ -110,7 +111,7 @@ mod tests {
let (_, decoded) = tokio_current_thread::block_on_all(data_sent.join(data_received)) let (_, decoded) = tokio_current_thread::block_on_all(data_sent.join(data_received))
.map_err(|_| ()) .map_err(|_| ())
.unwrap(); .unwrap();
assert_eq!(decoded.unwrap(), data); assert_eq!(&decoded.unwrap()[..], &data[..]);
} }
fn full_codec_encode_then_decode(cipher: Cipher) { fn full_codec_encode_then_decode(cipher: Cipher) {
@ -179,6 +180,11 @@ mod tests {
full_codec_encode_then_decode(Cipher::Aes256); full_codec_encode_then_decode(Cipher::Aes256);
} }
#[test]
fn full_codec_encode_then_decode_twofish() {
full_codec_encode_then_decode(Cipher::TwofishCtr);
}
#[test] #[test]
fn full_codec_encode_then_decode_null() { fn full_codec_encode_then_decode_null() {
full_codec_encode_then_decode(Cipher::Null); full_codec_encode_then_decode(Cipher::Null);

View File

@ -20,7 +20,7 @@
//! Defines the `SecioError` enum that groups all possible errors in SECIO. //! Defines the `SecioError` enum that groups all possible errors in SECIO.
use crypto::symmetriccipher::SymmetricCipherError; use aes_ctr::stream_cipher::LoopError;
use std::error; use std::error;
use std::fmt; use std::fmt;
use std::io::Error as IoError; use std::io::Error as IoError;
@ -55,8 +55,8 @@ pub enum SecioError {
/// The final check of the handshake failed. /// The final check of the handshake failed.
NonceVerificationFailed, NonceVerificationFailed,
/// Error while decoding/encoding data. /// Error with block cipher.
CipherError(SymmetricCipherError), CipherError(LoopError),
/// The received frame was of invalid length. /// The received frame was of invalid length.
FrameTooShort, FrameTooShort,
@ -115,9 +115,9 @@ impl fmt::Display for SecioError {
} }
} }
impl From<SymmetricCipherError> for SecioError { impl From<LoopError> for SecioError {
#[inline] #[inline]
fn from(err: SymmetricCipherError) -> SecioError { fn from(err: LoopError) -> SecioError {
SecioError::CipherError(err) SecioError::CipherError(err)
} }
} }

View File

@ -77,10 +77,11 @@
//! `SecioMiddleware` that implements `Sink` and `Stream` and can be used to send packets of data. //! `SecioMiddleware` that implements `Sink` and `Stream` and can be used to send packets of data.
//! //!
extern crate aes_ctr;
#[cfg(feature = "secp256k1")] #[cfg(feature = "secp256k1")]
extern crate asn1_der; extern crate asn1_der;
extern crate bytes; extern crate bytes;
extern crate crypto; extern crate ctr;
extern crate futures; extern crate futures;
extern crate libp2p_core; extern crate libp2p_core;
#[macro_use] #[macro_use]
@ -92,8 +93,12 @@ extern crate rw_stream_sink;
#[cfg(feature = "secp256k1")] #[cfg(feature = "secp256k1")]
extern crate secp256k1; extern crate secp256k1;
extern crate tokio_io; extern crate tokio_io;
extern crate twofish;
extern crate untrusted; extern crate untrusted;
#[cfg(feature = "aes-all")]
#[macro_use]
extern crate lazy_static;
pub use self::error::SecioError; pub use self::error::SecioError;
#[cfg(feature = "secp256k1")] #[cfg(feature = "secp256k1")]
@ -116,8 +121,8 @@ mod algo_support;
mod codec; mod codec;
mod error; mod error;
mod handshake; mod handshake;
mod stream_cipher;
mod structs_proto; mod structs_proto;
mod stream_cipher;
pub use algo_support::{Digest, KeyAgreement}; pub use algo_support::{Digest, KeyAgreement};
pub use stream_cipher::Cipher; pub use stream_cipher::Cipher;

View File

@ -19,14 +19,19 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use super::codec::StreamCipher; use super::codec::StreamCipher;
use crypto::{aessafe, blockmodes::CtrModeX8, symmetriccipher::SynchronousStreamCipher}; use aes_ctr::stream_cipher::generic_array::GenericArray;
use aes_ctr::stream_cipher::{NewFixStreamCipher, LoopError, StreamCipherCore};
use aes_ctr::{Aes128Ctr, Aes256Ctr};
use ctr::Ctr128;
use twofish::Twofish;
/// Possible encryption ciphers. /// Possible encryption ciphers.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum Cipher { pub enum Cipher {
Aes128, Aes128,
Aes256, Aes256,
Null TwofishCtr,
Null,
} }
impl Cipher { impl Cipher {
@ -35,7 +40,8 @@ impl Cipher {
match *self { match *self {
Cipher::Aes128 => 16, Cipher::Aes128 => 16,
Cipher::Aes256 => 32, Cipher::Aes256 => 32,
Cipher::Null => 0 Cipher::TwofishCtr => 32,
Cipher::Null => 0,
} }
} }
@ -43,7 +49,7 @@ impl Cipher {
#[inline] #[inline]
pub fn iv_size(&self) -> usize { pub fn iv_size(&self) -> usize {
match self { match self {
Cipher::Aes128 | Cipher::Aes256 => 16, Cipher::Aes128 | Cipher::Aes256 | Cipher::TwofishCtr => 16,
Cipher::Null => 0 Cipher::Null => 0
} }
} }
@ -54,25 +60,123 @@ impl Cipher {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct NullCipher; pub struct NullCipher;
impl SynchronousStreamCipher for NullCipher { impl StreamCipherCore for NullCipher {
fn process(&mut self, input: &[u8], output: &mut [u8]) { fn try_apply_keystream(&mut self, _data: &mut [u8]) -> Result<(), LoopError> {
output.copy_from_slice(input) Ok(())
} }
} }
/// Returns your stream cipher depending on `Cipher`. /// Returns your stream cipher depending on `Cipher`.
#[inline] #[cfg(not(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86"))))]
pub fn ctr(c: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher { pub fn ctr(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher {
match c { ctr_int(key_size, key, iv)
Cipher::Aes128 => { }
let aes_dec = aessafe::AesSafe128EncryptorX8::new(key);
Box::new(CtrModeX8::new(aes_dec, iv)) /// Returns your stream cipher depending on `Cipher`.
}, #[cfg(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86")))]
Cipher::Aes256 => { pub fn ctr(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher {
let aes_dec = aessafe::AesSafe256EncryptorX8::new(key); if *aes_alt::AES_NI {
Box::new(CtrModeX8::new(aes_dec, iv)) aes_alt::ctr_alt(key_size, key, iv)
}, } else {
Cipher::Null => Box::new(NullCipher) ctr_int(key_size, key, iv)
} }
} }
#[cfg(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86")))]
mod aes_alt {
extern crate aesni;
use ::codec::StreamCipher;
use ctr::Ctr128;
use self::aesni::{Aes128, Aes256};
use ctr::stream_cipher::NewFixStreamCipher;
use ctr::stream_cipher::generic_array::GenericArray;
use twofish::Twofish;
use super::{Cipher, NullCipher};
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: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher {
match key_size {
Cipher::Aes128 => Box::new(Aes128Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
Cipher::Aes256 => Box::new(Aes256Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
Cipher::TwofishCtr => Box::new(Ctr128::<Twofish>::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
Cipher::Null => Box::new(NullCipher),
}
}
}
#[inline]
fn ctr_int(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher {
match key_size {
Cipher::Aes128 => Box::new(Aes128Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
Cipher::Aes256 => Box::new(Aes256Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
Cipher::TwofishCtr => Box::new(Ctr128::<Twofish>::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
Cipher::Null => Box::new(NullCipher),
}
}
#[cfg(all(
feature = "aes-all",
any(target_arch = "x86_64", target_arch = "x86"),
))]
#[cfg(test)]
mod tests {
use super::{Cipher, 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(Cipher::Aes128, &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."
);