Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for twofish cipher #457

Merged
merged 2 commits into from
Sep 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions protocols/secio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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" }
Expand Down
9 changes: 5 additions & 4 deletions protocols/secio/src/algo_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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!(
Expand Down
35 changes: 25 additions & 10 deletions protocols/secio/src/codec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
);
Expand All @@ -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();
Expand All @@ -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),
)
},
Expand All @@ -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),
)
});
Expand All @@ -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);
}
}
10 changes: 4 additions & 6 deletions protocols/secio/src/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions protocols/secio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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")]
Expand Down
68 changes: 48 additions & 20 deletions protocols/secio/src/stream_cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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")
Expand All @@ -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),
)),
Expand All @@ -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::<Twofish>::new(
GenericArray::from_slice(key),
GenericArray::from_slice(iv),
)),
Expand All @@ -105,15 +133,15 @@ 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() {
// 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(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());
Expand Down