Add support for twofish cipher (#457)

This commit is contained in:
Pierre Krieger 2018-09-05 02:15:16 +02:00 committed by GitHub
parent ba4bd79fd9
commit e2960b4317
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 42 deletions

View File

@ -15,7 +15,8 @@ rand = "0.3.17"
ring = { version = "0.12", features = ["rsa_signing"] } ring = { version = "0.12", features = ["rsa_signing"] }
aes-ctr = "0.1.0" aes-ctr = "0.1.0"
aesni = { version = "0.4.1", features = ["nocheck"], optional = true } 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 } 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 }
@ -25,7 +26,7 @@ untrusted = "0.5"
[features] [features]
default = ["secp256k1"] default = ["secp256k1"]
secp256k1 = ["eth-secp256k1"] secp256k1 = ["eth-secp256k1"]
aes-all = ["ctr","aesni","lazy_static"] aes-all = ["aesni","lazy_static"]
[dev-dependencies] [dev-dependencies]
libp2p-tcp-transport = { path = "../../transports/tcp" } libp2p-tcp-transport = { path = "../../transports/tcp" }

View File

@ -28,7 +28,7 @@ macro_rules! supported_impl {
pub mod $mod_name { pub mod $mod_name {
use std::cmp::Ordering; use std::cmp::Ordering;
#[allow(unused_imports)] #[allow(unused_imports)]
use stream_cipher::KeySize; use stream_cipher::Cipher;
#[allow(unused_imports)] #[allow(unused_imports)]
use ring::{agreement, digest}; use ring::{agreement, digest};
use error::SecioError; use error::SecioError;
@ -83,9 +83,10 @@ supported_impl!(
// TODO: the Go & JS implementations advertise Blowfish ; however doing so in Rust leads to // TODO: the Go & JS implementations advertise Blowfish ; however doing so in Rust leads to
// runtime errors // runtime errors
supported_impl!( supported_impl!(
ciphers: KeySize, ciphers: Cipher,
"AES-128" => KeySize::KeySize128, "AES-128" => Cipher::Aes128,
"AES-256" => KeySize::KeySize256, "AES-256" => Cipher::Aes256,
"TwofishCTR" => Cipher::Twofish,
); );
supported_impl!( supported_impl!(

View File

@ -64,7 +64,7 @@ mod tests {
extern crate tokio_tcp; extern crate tokio_tcp;
use self::tokio_tcp::TcpListener; use self::tokio_tcp::TcpListener;
use self::tokio_tcp::TcpStream; use self::tokio_tcp::TcpStream;
use stream_cipher::{ctr, KeySize}; use stream_cipher::{ctr, Cipher};
use super::full_codec; use super::full_codec;
use super::DecoderMiddleware; use super::DecoderMiddleware;
use super::EncoderMiddleware; use super::EncoderMiddleware;
@ -93,12 +93,12 @@ mod tests {
let encoder = EncoderMiddleware::new( let encoder = EncoderMiddleware::new(
data_tx, data_tx,
ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]), ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]),
SigningKey::new(&SHA256, &hmac_key), SigningKey::new(&SHA256, &hmac_key),
); );
let decoder = DecoderMiddleware::new( let decoder = DecoderMiddleware::new(
data_rx, data_rx,
ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]), ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]),
VerificationKey::new(&SHA256, &hmac_key), VerificationKey::new(&SHA256, &hmac_key),
32, 32,
); );
@ -114,11 +114,11 @@ mod tests {
assert_eq!(&decoded.unwrap()[..], &data[..]); assert_eq!(&decoded.unwrap()[..], &data[..]);
} }
#[test] fn full_codec_encode_then_decode(cipher: Cipher) {
fn full_codec_encode_then_decode() {
let cipher_key: [u8; 32] = rand::random(); let cipher_key: [u8; 32] = rand::random();
let cipher_key_clone = cipher_key.clone(); 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 hmac_key_clone = hmac_key.clone();
let data = b"hello world"; let data = b"hello world";
let data_clone = data.clone(); let data_clone = data.clone();
@ -132,9 +132,9 @@ mod tests {
full_codec( full_codec(
connec, connec,
ctr(KeySize::KeySize256, &cipher_key, &NULL_IV[..]), ctr(cipher, &cipher_key[..key_size], &NULL_IV[..]),
SigningKey::new(&SHA256, &hmac_key), 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), VerificationKey::new(&SHA256, &hmac_key),
) )
}, },
@ -147,9 +147,9 @@ mod tests {
full_codec( full_codec(
stream, stream,
ctr(KeySize::KeySize256, &cipher_key_clone, &NULL_IV[..]), ctr(cipher, &cipher_key_clone[..key_size], &NULL_IV[..]),
SigningKey::new(&SHA256, &hmac_key_clone), 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), VerificationKey::new(&SHA256, &hmac_key_clone),
) )
}); });
@ -169,4 +169,19 @@ mod tests {
let received = tokio_current_thread::block_on_all(fin).unwrap(); let received = tokio_current_thread::block_on_all(fin).unwrap();
assert_eq!(received, data); 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);
}
} }

View File

@ -21,7 +21,7 @@
use algo_support; use algo_support;
use bytes::BytesMut; use bytes::BytesMut;
use codec::{full_codec, FullCodec}; use codec::{full_codec, FullCodec};
use stream_cipher::{KeySize, ctr}; use stream_cipher::{Cipher, ctr};
use error::SecioError; use error::SecioError;
use futures::future; use futures::future;
use futures::sink::Sink; use futures::sink::Sink;
@ -102,7 +102,7 @@ where
// Crypto algorithms chosen for the communication. // Crypto algorithms chosen for the communication.
chosen_exchange: Option<&'static agreement::Algorithm>, chosen_exchange: Option<&'static agreement::Algorithm>,
// We only support AES for now, so store just a key size. // We only support AES for now, so store just a key size.
chosen_cipher: Option<KeySize>, chosen_cipher: Option<Cipher>,
chosen_hash: Option<&'static digest::Algorithm>, chosen_hash: Option<&'static digest::Algorithm>,
// Ephemeral key generated for the handshake and then thrown away. // 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 key = SigningKey::new(context.chosen_hash.unwrap(), key_material);
let chosen_cipher = context.chosen_cipher.unwrap(); let chosen_cipher = context.chosen_cipher.unwrap();
let (cipher_key_size, iv_size) = match chosen_cipher { let cipher_key_size = chosen_cipher.key_size();
KeySize::KeySize128 => (16, 16), let iv_size = chosen_cipher.iv_size();
KeySize::KeySize256 => (32, 16),
};
let mut longer_key = vec![0u8; 2 * (iv_size + cipher_key_size + 20)]; let mut longer_key = vec![0u8; 2 * (iv_size + cipher_key_size + 20)];
stretch_key(&key, &mut longer_key); stretch_key(&key, &mut longer_key);

View File

@ -82,6 +82,7 @@ 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 ctr;
extern crate futures; extern crate futures;
extern crate libp2p_core; extern crate libp2p_core;
#[macro_use] #[macro_use]
@ -93,6 +94,7 @@ 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")] #[cfg(feature = "aes-all")]

View File

@ -22,22 +22,42 @@ use super::codec::StreamCipher;
use aes_ctr::stream_cipher::generic_array::GenericArray; use aes_ctr::stream_cipher::generic_array::GenericArray;
use aes_ctr::stream_cipher::NewFixStreamCipher; use aes_ctr::stream_cipher::NewFixStreamCipher;
use aes_ctr::{Aes128Ctr, Aes256Ctr}; use aes_ctr::{Aes128Ctr, Aes256Ctr};
use ctr::Ctr128;
use twofish::Twofish;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum KeySize { pub enum Cipher {
KeySize128, Aes128,
KeySize256, 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"))))] #[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) 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")))] #[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 { if *aes_alt::AES_NI {
aes_alt::ctr_alt(key_size, key, iv) aes_alt::ctr_alt(key_size, key, iv)
} else { } 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")))] #[cfg(all(feature = "aes-all", any(target_arch = "x86_64", target_arch = "x86")))]
mod aes_alt { mod aes_alt {
extern crate ctr;
extern crate aesni; extern crate aesni;
use ::codec::StreamCipher; use ::codec::StreamCipher;
use self::ctr::Ctr128; use ctr::Ctr128;
use self::aesni::{Aes128, Aes256}; use self::aesni::{Aes128, Aes256};
use self::ctr::stream_cipher::NewFixStreamCipher; use ctr::stream_cipher::NewFixStreamCipher;
use self::ctr::stream_cipher::generic_array::GenericArray; use ctr::stream_cipher::generic_array::GenericArray;
use super::KeySize; use twofish::Twofish;
use super::Cipher;
lazy_static! { lazy_static! {
pub static ref AES_NI: bool = is_x86_feature_detected!("aes") pub static ref AES_NI: bool = is_x86_feature_detected!("aes")
@ -70,13 +90,17 @@ mod aes_alt {
pub type Aes256Ctr = Ctr128<Aes256>; pub type Aes256Ctr = Ctr128<Aes256>;
/// Returns alternate stream cipher if target functionalities does not allow standard one. /// Returns alternate stream cipher if target functionalities does not allow standard one.
/// Eg : aes without sse /// 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 { match key_size {
KeySize::KeySize128 => Box::new(Aes128Ctr::new( Cipher::Aes128 => Box::new(Aes128Ctr::new(
GenericArray::from_slice(key), GenericArray::from_slice(key),
GenericArray::from_slice(iv), 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::<Twofish>::new(
GenericArray::from_slice(key), GenericArray::from_slice(key),
GenericArray::from_slice(iv), GenericArray::from_slice(iv),
)), )),
@ -86,13 +110,17 @@ mod aes_alt {
} }
#[inline] #[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 { match key_size {
KeySize::KeySize128 => Box::new(Aes128Ctr::new( Cipher::Aes128 => Box::new(Aes128Ctr::new(
GenericArray::from_slice(key), GenericArray::from_slice(key),
GenericArray::from_slice(iv), 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::<Twofish>::new(
GenericArray::from_slice(key), GenericArray::from_slice(key),
GenericArray::from_slice(iv), GenericArray::from_slice(iv),
)), )),
@ -105,7 +133,7 @@ fn ctr_int(key_size: KeySize, key: &[u8], iv: &[u8]) -> StreamCipher {
))] ))]
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{KeySize, ctr}; use super::{Cipher, ctr};
#[test] #[test]
fn assert_non_native_run() { fn assert_non_native_run() {
@ -113,7 +141,7 @@ mod tests {
let key = [0;16]; let key = [0;16];
let iv = [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]; let mut content = [0;16];
assert!(aes assert!(aes
.try_apply_keystream(&mut content).is_ok()); .try_apply_keystream(&mut content).is_ok());