diff --git a/protocols/secio/Cargo.toml b/protocols/secio/Cargo.toml index 7f94cb89063..4c89caaab8d 100644 --- a/protocols/secio/Cargo.toml +++ b/protocols/secio/Cargo.toml @@ -15,7 +15,8 @@ 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 } -ctr = { version = "0.1", 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" } eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", optional = true } @@ -25,7 +26,7 @@ untrusted = "0.5" [features] default = ["secp256k1"] secp256k1 = ["eth-secp256k1"] -aes-all = ["ctr","aesni","lazy_static"] +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 8cff3725d41..ea73ea22865 100644 --- a/protocols/secio/src/algo_support.rs +++ b/protocols/secio/src/algo_support.rs @@ -28,7 +28,7 @@ macro_rules! supported_impl { pub mod $mod_name { use std::cmp::Ordering; #[allow(unused_imports)] - use stream_cipher::KeySize; + use stream_cipher::Cipher; #[allow(unused_imports)] use ring::{agreement, digest}; use error::SecioError; @@ -83,9 +83,10 @@ supported_impl!( // 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, + ciphers: Cipher, + "AES-128" => Cipher::Aes128, + "AES-256" => Cipher::Aes256, + "TwofishCTR" => Cipher::Twofish, ); supported_impl!( diff --git a/protocols/secio/src/codec/mod.rs b/protocols/secio/src/codec/mod.rs index 706342e1af4..78052774f20 100644 --- a/protocols/secio/src/codec/mod.rs +++ b/protocols/secio/src/codec/mod.rs @@ -64,7 +64,7 @@ mod tests { extern crate tokio_tcp; use self::tokio_tcp::TcpListener; use self::tokio_tcp::TcpStream; - use stream_cipher::{ctr, KeySize}; + use stream_cipher::{ctr, Cipher}; use super::full_codec; use super::DecoderMiddleware; use super::EncoderMiddleware; @@ -93,12 +93,12 @@ mod tests { let encoder = EncoderMiddleware::new( data_tx, - ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]), + ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]), SigningKey::new(&SHA256, &hmac_key), ); let decoder = DecoderMiddleware::new( data_rx, - ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]), + ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]), VerificationKey::new(&SHA256, &hmac_key), 32, ); @@ -114,11 +114,11 @@ mod tests { assert_eq!(&decoded.unwrap()[..], &data[..]); } - #[test] - fn full_codec_encode_then_decode() { + fn full_codec_encode_then_decode(cipher: Cipher) { let cipher_key: [u8; 32] = rand::random(); let cipher_key_clone = cipher_key.clone(); - let hmac_key: [u8; 32] = rand::random(); + let key_size = cipher.key_size(); + let hmac_key: [u8; 16] = rand::random(); let hmac_key_clone = hmac_key.clone(); let data = b"hello world"; let data_clone = data.clone(); @@ -132,9 +132,9 @@ mod tests { full_codec( connec, - ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]), + ctr(cipher, &cipher_key[..key_size], &NULL_IV[..]), SigningKey::new(&SHA256, &hmac_key), - ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]), + ctr(cipher, &cipher_key[..key_size], &NULL_IV[..]), VerificationKey::new(&SHA256, &hmac_key), ) }, @@ -147,9 +147,9 @@ mod tests { full_codec( stream, - ctr(KeySize::KeySize256, &cipher_key_clone, &NULL_IV[..]), + ctr(cipher, &cipher_key_clone[..key_size], &NULL_IV[..]), SigningKey::new(&SHA256, &hmac_key_clone), - ctr(KeySize::KeySize256, &cipher_key_clone, &NULL_IV[..]), + ctr(cipher, &cipher_key_clone[..key_size], &NULL_IV[..]), VerificationKey::new(&SHA256, &hmac_key_clone), ) }); @@ -169,4 +169,19 @@ mod tests { let received = tokio_current_thread::block_on_all(fin).unwrap(); assert_eq!(received, data); } + + #[test] + fn full_codec_encode_then_decode_aes128() { + full_codec_encode_then_decode(Cipher::Aes128); + } + + #[test] + 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/handshake.rs b/protocols/secio/src/handshake.rs index f7c163c60c3..7c98f72eee4 100644 --- a/protocols/secio/src/handshake.rs +++ b/protocols/secio/src/handshake.rs @@ -21,7 +21,7 @@ use algo_support; use bytes::BytesMut; use codec::{full_codec, FullCodec}; -use stream_cipher::{KeySize, ctr}; +use stream_cipher::{Cipher, ctr}; use error::SecioError; use futures::future; use futures::sink::Sink; @@ -102,7 +102,7 @@ where // 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, + chosen_cipher: Option, chosen_hash: Option<&'static digest::Algorithm>, // Ephemeral key generated for the handshake and then thrown away. @@ -453,10 +453,8 @@ where 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 cipher_key_size = chosen_cipher.key_size(); + let iv_size = chosen_cipher.iv_size(); let mut longer_key = vec![0u8; 2 * (iv_size + cipher_key_size + 20)]; stretch_key(&key, &mut longer_key); diff --git a/protocols/secio/src/lib.rs b/protocols/secio/src/lib.rs index 5476e5e9c20..c8452e525a2 100644 --- a/protocols/secio/src/lib.rs +++ b/protocols/secio/src/lib.rs @@ -82,6 +82,7 @@ extern crate aes_ctr; #[cfg(feature = "secp256k1")] extern crate asn1_der; extern crate bytes; +extern crate ctr; extern crate futures; extern crate libp2p_core; #[macro_use] @@ -93,6 +94,7 @@ 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")] diff --git a/protocols/secio/src/stream_cipher.rs b/protocols/secio/src/stream_cipher.rs index 8889dce5d87..3eca41d0162 100644 --- a/protocols/secio/src/stream_cipher.rs +++ b/protocols/secio/src/stream_cipher.rs @@ -22,22 +22,42 @@ 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; #[derive(Clone, Copy)] -pub enum KeySize { - KeySize128, - KeySize256, +pub enum Cipher { + Aes128, + Aes256, + Twofish, } -/// Returns your stream cipher depending on `KeySize`. +impl Cipher { + /// Returns the size of in bytes of the key expected by the cipher. + pub fn key_size(&self) -> usize { + match *self { + Cipher::Aes128 => 16, + Cipher::Aes256 => 32, + Cipher::Twofish => 32, + } + } + + /// Returns the size of in bytes of the IV expected by the cipher. + #[inline] + pub fn iv_size(&self) -> usize { + 16 // CTR 128 + } +} + +/// 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: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher { +pub fn ctr(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher { ctr_int(key_size, key, iv) } -/// Returns your stream cipher depending on `KeySize`. +/// 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: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher { +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 { @@ -48,14 +68,14 @@ pub fn ctr(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher { #[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 ctr::Ctr128; use self::aesni::{Aes128, Aes256}; - use self::ctr::stream_cipher::NewFixStreamCipher; - use self::ctr::stream_cipher::generic_array::GenericArray; - use super::KeySize; + 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") @@ -70,13 +90,17 @@ mod aes_alt { 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: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher { + pub fn ctr_alt(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher { match key_size { - KeySize::KeySize128 => Box::new(Aes128Ctr::new( + Cipher::Aes128 => Box::new(Aes128Ctr::new( GenericArray::from_slice(key), GenericArray::from_slice(iv), )), - KeySize::KeySize256 => Box::new(Aes256Ctr::new( + 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), )), @@ -86,13 +110,17 @@ mod aes_alt { } #[inline] -fn ctr_int(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher { +fn ctr_int(key_size: Cipher, key: &[u8], iv: &[u8]) -> StreamCipher { match key_size { - KeySize::KeySize128 => Box::new(Aes128Ctr::new( + 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), )), - KeySize::KeySize256 => Box::new(Aes256Ctr::new( + Cipher::Twofish => Box::new(Ctr128::::new( GenericArray::from_slice(key), GenericArray::from_slice(iv), )), @@ -105,7 +133,7 @@ fn ctr_int(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher { ))] #[cfg(test)] mod tests { - use super::{KeySize, ctr}; + use super::{Cipher, ctr}; #[test] fn assert_non_native_run() { @@ -113,7 +141,7 @@ mod tests { let key = [0;16]; let iv = [0;16]; - let mut aes = ctr(KeySize::KeySize128, &key, &iv); + let mut aes = ctr(Cipher::Aes128, &key, &iv); let mut content = [0;16]; assert!(aes .try_apply_keystream(&mut content).is_ok());