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"] }
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" }

View File

@ -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!(

View File

@ -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);
}
}

View File

@ -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<KeySize>,
chosen_cipher: Option<Cipher>,
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);

View File

@ -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")]

View File

@ -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<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 {
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::<Twofish>::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),
)),
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(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());