From 0a3d4cdfade1656d72ca99751e94e179bdaec221 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Fri, 7 Sep 2018 14:05:42 +0200 Subject: [PATCH] Revert "remove rustc-serialize (#359) (#386)" (#466) This reverts commit 73996885cb2d645ee91361204c33e7039290aabc. --- protocols/secio/Cargo.toml | 7 +- protocols/secio/src/algo_support.rs | 3 - protocols/secio/src/codec/decode.rs | 26 +++--- protocols/secio/src/codec/encode.rs | 23 +++-- protocols/secio/src/codec/mod.rs | 14 +-- protocols/secio/src/error.rs | 10 +-- protocols/secio/src/handshake.rs | 4 +- protocols/secio/src/lib.rs | 9 +- protocols/secio/src/stream_cipher.rs | 124 +++------------------------ 9 files changed, 50 insertions(+), 170 deletions(-) diff --git a/protocols/secio/Cargo.toml b/protocols/secio/Cargo.toml index 4c89caaa..ca90e7c7 100644 --- a/protocols/secio/Cargo.toml +++ b/protocols/secio/Cargo.toml @@ -13,11 +13,7 @@ log = "0.4.1" protobuf = "2.0.2" rand = "0.3.17" ring = { version = "0.12", features = ["rsa_signing"] } -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 } +rust-crypto = "^0.2" rw-stream-sink = { path = "../../misc/rw-stream-sink" } eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", optional = true } tokio-io = "0.1.0" @@ -26,7 +22,6 @@ untrusted = "0.5" [features] default = ["secp256k1"] secp256k1 = ["eth-secp256k1"] -aes-all = ["aesni","lazy_static"] [dev-dependencies] libp2p-tcp-transport = { path = "../../transports/tcp" } diff --git a/protocols/secio/src/algo_support.rs b/protocols/secio/src/algo_support.rs index 26585e87..4931b2d9 100644 --- a/protocols/secio/src/algo_support.rs +++ b/protocols/secio/src/algo_support.rs @@ -85,11 +85,8 @@ supported_impl!( // TODO: the Go & JS implementations advertise Blowfish ; however doing so in Rust leads to // runtime errors -// TODO: the AES library we're using seems to have a bug causing data corruption from time to time, -// which is why we prioritize TwoFish supported_impl!( ciphers: Cipher, - "TwofishCTR" => Cipher::Twofish, "AES-128" => Cipher::Aes128, "AES-256" => Cipher::Aes256, ); diff --git a/protocols/secio/src/codec/decode.rs b/protocols/secio/src/codec/decode.rs index 08440022..d32bfe50 100644 --- a/protocols/secio/src/codec/decode.rs +++ b/protocols/secio/src/codec/decode.rs @@ -21,8 +21,7 @@ //! Individual messages decoding. use bytes::BytesMut; -use super::StreamCipher; - +use codec::StreamCipher; use error::SecioError; use futures::sink::Sink; use futures::stream::Stream; @@ -88,24 +87,21 @@ where 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 (crypted_data, expected_hash) = frame.split_at(frame.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() { + debug!("hmac mismatch when decoding secio frame"); + return Err(SecioError::HmacNotMatching); } - let mut data_buf = frame.to_vec(); - data_buf.truncate(content_length); + // Note that there is no way to decipher in place with rust-crypto right now. + let mut decrypted_data = crypted_data.to_vec(); self.cipher_state - .try_apply_keystream(&mut data_buf) - .map_err::(|e|e.into())?; + .process(&crypted_data, &mut decrypted_data); - Ok(Async::Ready(Some(data_buf))) + Ok(Async::Ready(Some(decrypted_data))) } } diff --git a/protocols/secio/src/codec/encode.rs b/protocols/secio/src/codec/encode.rs index f163ea68..103a736d 100644 --- a/protocols/secio/src/codec/encode.rs +++ b/protocols/secio/src/codec/encode.rs @@ -21,7 +21,7 @@ //! Individual messages encoding. use bytes::BytesMut; -use super::StreamCipher; +use codec::StreamCipher; use futures::sink::Sink; use futures::stream::Stream; use futures::Poll; @@ -62,15 +62,22 @@ where type SinkItem = BytesMut; type SinkError = S::SinkError; - fn start_send(&mut self, mut data_buf: Self::SinkItem) -> StartSend { + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + let capacity = item.len() + self.hmac_key.digest_algorithm().output_len; - // 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) + // Apparently this is the fastest way of doing. + // See https://gist.github.com/kirushik/e0d93759b0cd102f814408595c20a9d0 + let mut out_buffer = BytesMut::from(vec![0; capacity]); + { + 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] diff --git a/protocols/secio/src/codec/mod.rs b/protocols/secio/src/codec/mod.rs index 78052774..9764aa15 100644 --- a/protocols/secio/src/codec/mod.rs +++ b/protocols/secio/src/codec/mod.rs @@ -24,7 +24,7 @@ use self::decode::DecoderMiddleware; use self::encode::EncoderMiddleware; -use aes_ctr::stream_cipher::StreamCipherCore; +use crypto::symmetriccipher::SynchronousStreamCipher; use ring::hmac; use tokio_io::codec::length_delimited; use tokio_io::{AsyncRead, AsyncWrite}; @@ -35,7 +35,7 @@ mod encode; /// Type returned by `full_codec`. pub type FullCodec = DecoderMiddleware>>; -pub type StreamCipher = Box; +pub type StreamCipher = Box; /// Takes control of `socket`. Returns an object that implements `future::Sink` and @@ -79,7 +79,7 @@ mod tests { use std::io::Error as IoError; use tokio_io::codec::length_delimited::Framed; - const NULL_IV : [u8; 16] = [0;16]; + const NULL_IV : [u8; 16] = [0; 16]; #[test] fn raw_encode_then_decode() { @@ -90,7 +90,6 @@ mod tests { let cipher_key: [u8; 32] = rand::random(); let hmac_key: [u8; 32] = rand::random(); - let encoder = EncoderMiddleware::new( data_tx, ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]), @@ -111,7 +110,7 @@ mod tests { let (_, decoded) = tokio_current_thread::block_on_all(data_sent.join(data_received)) .map_err(|_| ()) .unwrap(); - assert_eq!(&decoded.unwrap()[..], &data[..]); + assert_eq!(decoded.unwrap(), data); } fn full_codec_encode_then_decode(cipher: Cipher) { @@ -179,9 +178,4 @@ mod tests { fn full_codec_encode_then_decode_aes256() { full_codec_encode_then_decode(Cipher::Aes256); } - - #[test] - fn full_codec_encode_then_decode_twofish() { - full_codec_encode_then_decode(Cipher::Twofish); - } } diff --git a/protocols/secio/src/error.rs b/protocols/secio/src/error.rs index a2bde175..dd6adf31 100644 --- a/protocols/secio/src/error.rs +++ b/protocols/secio/src/error.rs @@ -20,7 +20,7 @@ //! Defines the `SecioError` enum that groups all possible errors in SECIO. -use aes_ctr::stream_cipher::LoopError; +use crypto::symmetriccipher::SymmetricCipherError; use std::error; use std::fmt; use std::io::Error as IoError; @@ -55,8 +55,8 @@ pub enum SecioError { /// The final check of the handshake failed. NonceVerificationFailed, - /// Error with block cipher. - CipherError(LoopError), + /// Error while decoding/encoding data. + CipherError(SymmetricCipherError), /// The received frame was of invalid length. FrameTooShort, @@ -111,9 +111,9 @@ impl fmt::Display for SecioError { } } -impl From for SecioError { +impl From for SecioError { #[inline] - fn from(err: LoopError) -> SecioError { + fn from(err: SymmetricCipherError) -> SecioError { SecioError::CipherError(err) } } diff --git a/protocols/secio/src/handshake.rs b/protocols/secio/src/handshake.rs index c7da0143..27b365cb 100644 --- a/protocols/secio/src/handshake.rs +++ b/protocols/secio/src/handshake.rs @@ -484,8 +484,8 @@ where (cipher, hmac) }; - Ok(full_codec(socket, encoding_cipher, encoding_hmac, - decoding_cipher, decoding_hmac)) + Ok(full_codec(socket, encoding_cipher, encoding_hmac, decoding_cipher, + decoding_hmac)) }); match codec { diff --git a/protocols/secio/src/lib.rs b/protocols/secio/src/lib.rs index 0f88f94e..bbdc153b 100644 --- a/protocols/secio/src/lib.rs +++ b/protocols/secio/src/lib.rs @@ -78,11 +78,10 @@ //! `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 ctr; +extern crate crypto; extern crate futures; extern crate libp2p_core; #[macro_use] @@ -94,12 +93,8 @@ extern crate rw_stream_sink; #[cfg(feature = "secp256k1")] extern crate secp256k1; extern crate tokio_io; -extern crate twofish; extern crate untrusted; -#[cfg(feature = "aes-all")] -#[macro_use] -extern crate lazy_static; pub use self::error::SecioError; #[cfg(feature = "secp256k1")] @@ -122,8 +117,8 @@ mod algo_support; mod codec; mod error; mod handshake; -mod structs_proto; mod stream_cipher; +mod structs_proto; /// Implementation of the `ConnectionUpgrade` trait of `libp2p_core`. Automatically applies /// secio on any connection. diff --git a/protocols/secio/src/stream_cipher.rs b/protocols/secio/src/stream_cipher.rs index 3eca41d0..43cb35a5 100644 --- a/protocols/secio/src/stream_cipher.rs +++ b/protocols/secio/src/stream_cipher.rs @@ -19,17 +19,12 @@ // 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}; -use ctr::Ctr128; -use twofish::Twofish; +use crypto::{aessafe, blockmodes::CtrModeX8}; #[derive(Clone, Copy)] pub enum Cipher { Aes128, Aes256, - Twofish, } impl Cipher { @@ -38,7 +33,6 @@ impl Cipher { match *self { Cipher::Aes128 => 16, Cipher::Aes256 => 32, - Cipher::Twofish => 32, } } @@ -50,114 +44,16 @@ impl Cipher { } /// Returns your stream cipher depending on `Cipher`. -#[cfg(not(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86"))))] -pub fn ctr(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher { - ctr_int(key_size, key, iv) -} - -/// Returns your stream cipher depending on `Cipher`. -#[cfg(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86")))] -pub fn ctr(key_size: Cipher, 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 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; - - 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; - /// AES-256 in CTR mode - pub type Aes256Ctr = Ctr128; - /// 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::Twofish => Box::new(Ctr128::::new( - GenericArray::from_slice(key), - GenericArray::from_slice(iv), - )), - } - } - -} - #[inline] -fn ctr_int(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher { +pub fn ctr(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::Twofish => Box::new(Ctr128::::new( - GenericArray::from_slice(key), - GenericArray::from_slice(iv), - )), + Cipher::Aes128 => { + let aes_dec = aessafe::AesSafe128EncryptorX8::new(key); + Box::new(CtrModeX8::new(aes_dec, iv)) + }, + Cipher::Aes256 => { + let aes_dec = aessafe::AesSafe256EncryptorX8::new(key); + Box::new(CtrModeX8::new(aes_dec, iv)) + }, } } - -#[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." -);