Fix concerns

This commit is contained in:
Pierre Krieger
2017-11-13 10:27:33 +01:00
parent 2faf4875b7
commit f6e5da5358
7 changed files with 573 additions and 308 deletions

View File

@ -24,72 +24,72 @@
//! helps you with.
macro_rules! supported_impl {
($mod_name:ident: $ty:ty, $($name:expr => $val:expr),*,) => (
pub mod $mod_name {
use std::cmp::Ordering;
#[allow(unused_imports)]
use crypto::aes::KeySize;
#[allow(unused_imports)]
use ring::{agreement, digest};
use error::SecioError;
($mod_name:ident: $ty:ty, $($name:expr => $val:expr),*,) => (
pub mod $mod_name {
use std::cmp::Ordering;
#[allow(unused_imports)]
use crypto::aes::KeySize;
#[allow(unused_imports)]
use ring::{agreement, digest};
use error::SecioError;
/// String to advertise to the remote.
pub const PROPOSITION_STRING: &'static str = concat_comma!($($name),*);
/// String to advertise to the remote.
pub const PROPOSITION_STRING: &'static str = concat_comma!($($name),*);
/// Choose which algorithm to use based on the remote's advertised list.
pub fn select_best(hashes_ordering: Ordering, input: &str) -> Result<$ty, SecioError> {
match hashes_ordering {
Ordering::Less | Ordering::Equal => {
for second_elem in input.split(',') {
$(
if $name == second_elem {
return Ok($val);
}
)+
}
},
Ordering::Greater => {
$(
for second_elem in input.split(',') {
if $name == second_elem {
return Ok($val);
}
}
)+
},
};
/// Choose which algorithm to use based on the remote's advertised list.
pub fn select_best(hashes_ordering: Ordering, input: &str) -> Result<$ty, SecioError> {
match hashes_ordering {
Ordering::Less | Ordering::Equal => {
for second_elem in input.split(',') {
$(
if $name == second_elem {
return Ok($val);
}
)+
}
},
Ordering::Greater => {
$(
for second_elem in input.split(',') {
if $name == second_elem {
return Ok($val);
}
}
)+
},
};
Err(SecioError::NoSupportIntersection(PROPOSITION_STRING, input.to_owned()))
}
}
);
Err(SecioError::NoSupportIntersection(PROPOSITION_STRING, input.to_owned()))
}
}
);
}
// Concatenates several strings with commas.
macro_rules! concat_comma {
($first:expr, $($rest:expr),*) => (
concat!($first $(, ',', $rest)*)
);
($first:expr, $($rest:expr),*) => (
concat!($first $(, ',', $rest)*)
);
}
// TODO: there's no library in the Rust ecosystem that supports P-521, but the Go & JS
// implementations advertise it
supported_impl!(
exchanges: &'static agreement::Algorithm,
"P-256" => &agreement::ECDH_P256,
"P-384" => &agreement::ECDH_P384,
exchanges: &'static agreement::Algorithm,
"P-256" => &agreement::ECDH_P256,
"P-384" => &agreement::ECDH_P384,
);
// 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: KeySize,
"AES-128" => KeySize::KeySize128,
"AES-256" => KeySize::KeySize256,
);
supported_impl!(
hashes: &'static digest::Algorithm,
"SHA256" => &digest::SHA256,
"SHA512" => &digest::SHA512,
hashes: &'static digest::Algorithm,
"SHA256" => &digest::SHA256,
"SHA512" => &digest::SHA512,
);

View File

@ -62,7 +62,7 @@ impl<S> DecoderMiddleware<S> {
impl<S> Stream for DecoderMiddleware<S>
where S: Stream<Item = BytesMut>,
S::Error: Into<SecioError>
S::Error: Into<SecioError>
{
type Item = Vec<u8>;
type Error = SecioError;
@ -98,7 +98,7 @@ impl<S> Stream for DecoderMiddleware<S>
}
impl<S> Sink for DecoderMiddleware<S>
where S: Sink
where S: Sink
{
type SinkItem = S::SinkItem;
type SinkError = S::SinkError;

View File

@ -28,6 +28,7 @@ use futures::StartSend;
use futures::sink::Sink;
use futures::stream::Stream;
use ring::hmac;
use std::iter;
/// Wraps around a `Sink`. Encodes the buffers passed to it and passes it to the underlying sink.
///
@ -57,7 +58,7 @@ impl<S> EncoderMiddleware<S> {
}
impl<S> Sink for EncoderMiddleware<S>
where S: Sink<SinkItem = BytesMut>
where S: Sink<SinkItem = BytesMut>
{
type SinkItem = BytesMut;
type SinkError = S::SinkError;
@ -68,7 +69,7 @@ impl<S> Sink for EncoderMiddleware<S>
let mut out_buffer = BytesMut::with_capacity(capacity);
// Note: Alternatively to `extend`, we could also call `advance_mut()`, which will add
// uninitialized bytes to the buffer. But that's unsafe.
out_buffer.extend((0..item.len()).map(|_| 0));
out_buffer.extend(iter::repeat(0).take(item.len()));
self.cipher_state.process(&item, &mut out_buffer);
let signature = hmac::sign(&self.hmac_key, &out_buffer);
@ -86,7 +87,7 @@ impl<S> Sink for EncoderMiddleware<S>
}
impl<S> Stream for EncoderMiddleware<S>
where S: Stream
where S: Stream
{
type Item = S::Item;
type Error = S::Error;

View File

@ -150,12 +150,12 @@ mod tests {
});
let fin = server.join(client)
.from_err::<SecioError>()
.and_then(|(server, client)| {
.from_err::<SecioError>()
.and_then(|(server, client)| {
client.send(BytesMut::from(&data_clone[..])).map(move |_| server).from_err()
})
.and_then(|server| server.into_future().map_err(|(e, _)| e.into()))
.map(|recved| recved.0.unwrap().to_vec());
.and_then(|server| server.into_future().map_err(|(e, _)| e.into()))
.map(|recved| recved.0.unwrap().to_vec());
let received = core.run(fin).unwrap();
assert_eq!(received, data);

View File

@ -132,283 +132,283 @@ pub fn handshake<'a, S: 'a>(
length_delimited::Builder::new().big_endian().length_field_length(4).new_framed(socket);
let future = future::ok::<_, SecioError>(context)
// Generate our nonce.
.and_then(|mut context| {
context.rng.fill(&mut context.local_nonce)
.map_err(|_| SecioError::NonceGenerationFailed)?;
Ok(context)
})
// Generate our nonce.
.and_then(|mut context| {
context.rng.fill(&mut context.local_nonce)
.map_err(|_| SecioError::NonceGenerationFailed)?;
Ok(context)
})
// Send our proposition with our nonce, public key and supported protocols.
.and_then(|mut context| {
let mut public_key = PublicKeyProtobuf::new();
public_key.set_Type(KeyTypeProtobuf::RSA);
public_key.set_Data(context.local_public_key.clone());
context.local_public_key_in_protobuf_bytes = public_key.write_to_bytes().unwrap();
// Send our proposition with our nonce, public key and supported protocols.
.and_then(|mut context| {
let mut public_key = PublicKeyProtobuf::new();
public_key.set_Type(KeyTypeProtobuf::RSA);
public_key.set_Data(context.local_public_key.clone());
context.local_public_key_in_protobuf_bytes = public_key.write_to_bytes().unwrap();
let mut proposition = Propose::new();
proposition.set_rand(context.local_nonce.clone().to_vec());
proposition.set_pubkey(context.local_public_key_in_protobuf_bytes.clone());
proposition.set_exchanges(algo_support::exchanges::PROPOSITION_STRING.into());
proposition.set_ciphers(algo_support::ciphers::PROPOSITION_STRING.into());
proposition.set_hashes(algo_support::hashes::PROPOSITION_STRING.into());
let proposition_bytes = proposition.write_to_bytes().unwrap();
context.local_proposition_bytes = proposition_bytes.clone();
let mut proposition = Propose::new();
proposition.set_rand(context.local_nonce.clone().to_vec());
proposition.set_pubkey(context.local_public_key_in_protobuf_bytes.clone());
proposition.set_exchanges(algo_support::exchanges::PROPOSITION_STRING.into());
proposition.set_ciphers(algo_support::ciphers::PROPOSITION_STRING.into());
proposition.set_hashes(algo_support::hashes::PROPOSITION_STRING.into());
let proposition_bytes = proposition.write_to_bytes().unwrap();
context.local_proposition_bytes = proposition_bytes.clone();
socket.send(BytesMut::from(proposition_bytes.clone()))
.from_err()
.map(|s| (s, context))
})
socket.send(BytesMut::from(proposition_bytes.clone()))
.from_err()
.map(|s| (s, context))
})
// Receive the remote's proposition.
.and_then(move |(socket, mut context)| {
socket.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(prop_raw, socket)| {
match prop_raw {
Some(p) => context.remote_proposition_bytes = p,
None => {
let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
return Err(err.into())
},
};
// Receive the remote's proposition.
.and_then(move |(socket, mut context)| {
socket.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(prop_raw, socket)| {
match prop_raw {
Some(p) => context.remote_proposition_bytes = p,
None => {
let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
return Err(err.into())
},
};
let mut prop = {
protobuf_parse_from_bytes::<Propose>(&context.remote_proposition_bytes)
.map_err(|_| SecioError::HandshakeParsingFailure)?
};
context.remote_public_key_in_protobuf_bytes = prop.take_pubkey();
let mut pubkey = {
let bytes = &context.remote_public_key_in_protobuf_bytes;
protobuf_parse_from_bytes::<PublicKeyProtobuf>(bytes)
.map_err(|_| SecioError::HandshakeParsingFailure)?
};
let mut prop = {
protobuf_parse_from_bytes::<Propose>(&context.remote_proposition_bytes)
.map_err(|_| SecioError::HandshakeParsingFailure)?
};
context.remote_public_key_in_protobuf_bytes = prop.take_pubkey();
let mut pubkey = {
let bytes = &context.remote_public_key_in_protobuf_bytes;
protobuf_parse_from_bytes::<PublicKeyProtobuf>(bytes)
.map_err(|_| SecioError::HandshakeParsingFailure)?
};
// TODO: For now we suppose that the key is in the RSA format because that's
// the only thing the Go and JS implementations support.
match pubkey.get_Type() {
KeyTypeProtobuf::RSA => (),
_ => {
let err = IoError::new(IoErrorKind::Other, "unsupported protocol");
return Err(err.into());
},
};
context.remote_public_key = pubkey.take_Data();
context.remote_nonce = prop.take_rand();
Ok((prop, socket, context))
})
})
// TODO: For now we suppose that the key is in the RSA format because that's
// the only thing the Go and JS implementations support.
match pubkey.get_Type() {
KeyTypeProtobuf::RSA => (),
_ => {
let err = IoError::new(IoErrorKind::Other, "unsupported protocol");
return Err(err.into());
},
};
context.remote_public_key = pubkey.take_Data();
context.remote_nonce = prop.take_rand();
Ok((prop, socket, context))
})
})
// Decide which algorithms to use (thanks to the remote's proposition).
.and_then(move |(remote_prop, socket, mut context)| {
// In order to determine which protocols to use, we compute two hashes and choose
// based on which hash is larger.
context.hashes_ordering = {
let oh1 = {
let mut ctx = digest::Context::new(&digest::SHA256);
ctx.update(&context.remote_public_key_in_protobuf_bytes);
ctx.update(&context.local_nonce);
ctx.finish()
};
// Decide which algorithms to use (thanks to the remote's proposition).
.and_then(move |(remote_prop, socket, mut context)| {
// In order to determine which protocols to use, we compute two hashes and choose
// based on which hash is larger.
context.hashes_ordering = {
let oh1 = {
let mut ctx = digest::Context::new(&digest::SHA256);
ctx.update(&context.remote_public_key_in_protobuf_bytes);
ctx.update(&context.local_nonce);
ctx.finish()
};
let oh2 = {
let mut ctx = digest::Context::new(&digest::SHA256);
ctx.update(&context.local_public_key_in_protobuf_bytes);
ctx.update(&context.remote_nonce);
ctx.finish()
};
let oh2 = {
let mut ctx = digest::Context::new(&digest::SHA256);
ctx.update(&context.local_public_key_in_protobuf_bytes);
ctx.update(&context.remote_nonce);
ctx.finish()
};
oh1.as_ref().cmp(&oh2.as_ref())
};
oh1.as_ref().cmp(&oh2.as_ref())
};
context.chosen_exchange = {
let list = &remote_prop.get_exchanges();
Some(algo_support::exchanges::select_best(context.hashes_ordering, list)?)
};
context.chosen_cipher = {
let list = &remote_prop.get_ciphers();
Some(algo_support::ciphers::select_best(context.hashes_ordering, list)?)
};
context.chosen_hash = {
let list = &remote_prop.get_hashes();
Some(algo_support::hashes::select_best(context.hashes_ordering, list)?)
};
context.chosen_exchange = {
let list = &remote_prop.get_exchanges();
Some(algo_support::exchanges::select_best(context.hashes_ordering, list)?)
};
context.chosen_cipher = {
let list = &remote_prop.get_ciphers();
Some(algo_support::ciphers::select_best(context.hashes_ordering, list)?)
};
context.chosen_hash = {
let list = &remote_prop.get_hashes();
Some(algo_support::hashes::select_best(context.hashes_ordering, list)?)
};
Ok((socket, context))
})
Ok((socket, context))
})
// Generate an ephemeral key for the negotiation.
.and_then(|(socket, context)| {
let tmp_priv_key = EphemeralPrivateKey::generate(&agreement::ECDH_P256, &context.rng)
.map_err(|_| SecioError::EphemeralKeyGenerationFailed)?;
Ok((socket, context, tmp_priv_key))
})
// Generate an ephemeral key for the negotiation.
.and_then(|(socket, context)| {
let tmp_priv_key = EphemeralPrivateKey::generate(&agreement::ECDH_P256, &context.rng)
.map_err(|_| SecioError::EphemeralKeyGenerationFailed)?;
Ok((socket, context, tmp_priv_key))
})
// Send the ephemeral pub key to the remote in an `Exchange` struct. The `Exchange` also
// contains a signature of the two propositions encoded with our static public key.
.and_then(|(socket, mut context, tmp_priv)| {
let exchange = {
let local_tmp_pub_key = &mut context.local_tmp_pub_key[..tmp_priv.public_key_len()];
tmp_priv.compute_public_key(local_tmp_pub_key).unwrap();
context.local_tmp_priv_key = Some(tmp_priv);
// Send the ephemeral pub key to the remote in an `Exchange` struct. The `Exchange` also
// contains a signature of the two propositions encoded with our static public key.
.and_then(|(socket, mut context, tmp_priv)| {
let exchange = {
let local_tmp_pub_key = &mut context.local_tmp_pub_key[..tmp_priv.public_key_len()];
tmp_priv.compute_public_key(local_tmp_pub_key).unwrap();
context.local_tmp_priv_key = Some(tmp_priv);
let mut data_to_sign = context.local_proposition_bytes.clone();
data_to_sign.extend_from_slice(&context.remote_proposition_bytes);
data_to_sign.extend_from_slice(local_tmp_pub_key);
let mut data_to_sign = context.local_proposition_bytes.clone();
data_to_sign.extend_from_slice(&context.remote_proposition_bytes);
data_to_sign.extend_from_slice(local_tmp_pub_key);
let mut exchange = Exchange::new();
exchange.set_epubkey(local_tmp_pub_key.to_vec());
exchange.set_signature({
let mut state = match RSASigningState::new(context.local_private_key.clone()) {
Ok(s) => s,
Err(_) => return Err(SecioError::SigningFailure),
};
let mut signature = vec![0; context.local_private_key.public_modulus_len()];
match state.sign(&RSA_PKCS1_SHA256, &context.rng, &data_to_sign,
&mut signature)
{
Ok(_) => (),
Err(_) => return Err(SecioError::SigningFailure),
};
let mut exchange = Exchange::new();
exchange.set_epubkey(local_tmp_pub_key.to_vec());
exchange.set_signature({
let mut state = match RSASigningState::new(context.local_private_key.clone()) {
Ok(s) => s,
Err(_) => return Err(SecioError::SigningFailure),
};
let mut signature = vec![0; context.local_private_key.public_modulus_len()];
match state.sign(&RSA_PKCS1_SHA256, &context.rng, &data_to_sign,
&mut signature)
{
Ok(_) => (),
Err(_) => return Err(SecioError::SigningFailure),
};
signature
});
exchange
};
signature
});
exchange
};
let local_exch = exchange.write_to_bytes()
.expect("can only fail if the protobuf msg is malformed, which can't happen for \
this message in particular");
Ok((BytesMut::from(local_exch), socket, context))
})
let local_exch = exchange.write_to_bytes()
.expect("can only fail if the protobuf msg is malformed, which can't happen for \
this message in particular");
Ok((BytesMut::from(local_exch), socket, context))
})
// Send our local `Exchange`.
.and_then(|(local_exch, socket, context)| {
socket.send(local_exch)
.from_err()
.map(|s| (s, context))
})
// Send our local `Exchange`.
.and_then(|(local_exch, socket, context)| {
socket.send(local_exch)
.from_err()
.map(|s| (s, context))
})
// Receive the remote's `Exchange`.
.and_then(move |(socket, context)| {
socket.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(raw, socket)| {
let raw = match raw {
Some(r) => r,
None => {
let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
return Err(err.into())
},
};
// Receive the remote's `Exchange`.
.and_then(move |(socket, context)| {
socket.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(raw, socket)| {
let raw = match raw {
Some(r) => r,
None => {
let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
return Err(err.into())
},
};
let remote_exch = protobuf_parse_from_bytes::<Exchange>(&raw)
.map_err(|_| SecioError::HandshakeParsingFailure)?;
Ok((remote_exch, socket, context))
})
})
let remote_exch = protobuf_parse_from_bytes::<Exchange>(&raw)
.map_err(|_| SecioError::HandshakeParsingFailure)?;
Ok((remote_exch, socket, context))
})
})
// Check the validity of the remote's `Exchange`. This verifies that the remote was really
// the sender of its proposition, and that it is the owner of both its global and ephemeral
// keys.
.and_then(|(remote_exch, socket, context)| {
let mut data_to_verify = context.remote_proposition_bytes.clone();
data_to_verify.extend_from_slice(&context.local_proposition_bytes);
data_to_verify.extend_from_slice(remote_exch.get_epubkey());
// Check the validity of the remote's `Exchange`. This verifies that the remote was really
// the sender of its proposition, and that it is the owner of both its global and ephemeral
// keys.
.and_then(|(remote_exch, socket, context)| {
let mut data_to_verify = context.remote_proposition_bytes.clone();
data_to_verify.extend_from_slice(&context.local_proposition_bytes);
data_to_verify.extend_from_slice(remote_exch.get_epubkey());
// TODO: The ring library doesn't like some stuff in our DER public key, therefore
// we scrap the first 24 bytes of the key. A proper fix would be to write a DER
// parser, but that's not trivial.
match signature_verify(&RSA_PKCS1_2048_8192_SHA256,
UntrustedInput::from(&context.remote_public_key[24..]),
UntrustedInput::from(&data_to_verify),
UntrustedInput::from(remote_exch.get_signature()))
{
Ok(()) => (),
Err(_) => return Err(SecioError::SignatureVerificationFailed),
}
// TODO: The ring library doesn't like some stuff in our DER public key, therefore
// we scrap the first 24 bytes of the key. A proper fix would be to write a DER
// parser, but that's not trivial.
match signature_verify(&RSA_PKCS1_2048_8192_SHA256,
UntrustedInput::from(&context.remote_public_key[24..]),
UntrustedInput::from(&data_to_verify),
UntrustedInput::from(remote_exch.get_signature()))
{
Ok(()) => (),
Err(_) => return Err(SecioError::SignatureVerificationFailed),
}
Ok((remote_exch, socket, context))
})
Ok((remote_exch, socket, context))
})
// Generate a key from the local ephemeral private key and the remote ephemeral public key,
// derive from it a ciper key, an iv, and a hmac key, and build the encoder/decoder.
.and_then(|(remote_exch, socket, mut context)| {
let local_priv_key = context.local_tmp_priv_key.take()
.expect("we filled this Option earlier, and extract it now");
let codec = agreement::agree_ephemeral(local_priv_key,
&context.chosen_exchange.clone().unwrap(),
UntrustedInput::from(remote_exch.get_epubkey()),
SecioError::SecretGenerationFailed,
|key_material| {
let key = SigningKey::new(context.chosen_hash.unwrap(), key_material);
// Generate a key from the local ephemeral private key and the remote ephemeral public key,
// derive from it a ciper key, an iv, and a hmac key, and build the encoder/decoder.
.and_then(|(remote_exch, socket, mut context)| {
let local_priv_key = context.local_tmp_priv_key.take()
.expect("we filled this Option earlier, and extract it now");
let codec = agreement::agree_ephemeral(local_priv_key,
&context.chosen_exchange.clone().unwrap(),
UntrustedInput::from(remote_exch.get_epubkey()),
SecioError::SecretGenerationFailed,
|key_material| {
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),
_ => panic!()
};
let chosen_cipher = context.chosen_cipher.unwrap();
let (cipher_key_size, iv_size) = match chosen_cipher {
KeySize::KeySize128 => (16, 16),
KeySize::KeySize256 => (32, 16),
_ => panic!()
};
let mut longer_key = vec![0u8; 2 * (iv_size + cipher_key_size + 20)];
stretch_key(&key, &mut longer_key);
let mut longer_key = vec![0u8; 2 * (iv_size + cipher_key_size + 20)];
stretch_key(&key, &mut longer_key);
let (local_infos, remote_infos) = {
let (first_half, second_half) = longer_key.split_at(longer_key.len() / 2);
match context.hashes_ordering {
Ordering::Equal => panic!(),
Ordering::Less => (second_half, first_half),
Ordering::Greater => (first_half, second_half),
}
};
let (local_infos, remote_infos) = {
let (first_half, second_half) = longer_key.split_at(longer_key.len() / 2);
match context.hashes_ordering {
Ordering::Equal => panic!(),
Ordering::Less => (second_half, first_half),
Ordering::Greater => (first_half, second_half),
}
};
let (encoding_cipher, encoding_hmac) = {
let (iv, rest) = local_infos.split_at(iv_size);
let (cipher_key, mac_key) = rest.split_at(cipher_key_size);
let hmac = SigningKey::new(&context.chosen_hash.clone().unwrap(), mac_key);
let cipher = ctr(chosen_cipher, cipher_key, iv);
(cipher, hmac)
};
let (encoding_cipher, encoding_hmac) = {
let (iv, rest) = local_infos.split_at(iv_size);
let (cipher_key, mac_key) = rest.split_at(cipher_key_size);
let hmac = SigningKey::new(&context.chosen_hash.clone().unwrap(), mac_key);
let cipher = ctr(chosen_cipher, cipher_key, iv);
(cipher, hmac)
};
let (decoding_cipher, decoding_hmac) = {
let (iv, rest) = remote_infos.split_at(iv_size);
let (cipher_key, mac_key) = rest.split_at(cipher_key_size);
let hmac = VerificationKey::new(&context.chosen_hash.clone().unwrap(), mac_key);
let cipher = ctr(chosen_cipher, cipher_key, iv);
(cipher, hmac)
};
let (decoding_cipher, decoding_hmac) = {
let (iv, rest) = remote_infos.split_at(iv_size);
let (cipher_key, mac_key) = rest.split_at(cipher_key_size);
let hmac = VerificationKey::new(&context.chosen_hash.clone().unwrap(), mac_key);
let cipher = ctr(chosen_cipher, cipher_key, iv);
(cipher, hmac)
};
Ok(full_codec(socket, Box::new(encoding_cipher), encoding_hmac,
Box::new(decoding_cipher), decoding_hmac))
})?;
Ok(full_codec(socket, Box::new(encoding_cipher), encoding_hmac,
Box::new(decoding_cipher), decoding_hmac))
})?;
Ok((codec, context))
})
Ok((codec, context))
})
// We send back their nonce to check if the connection works.
.and_then(|(codec, mut context)| {
let remote_nonce = mem::replace(&mut context.remote_nonce, Vec::new());
codec.send(BytesMut::from(remote_nonce))
.map(|s| (s, context))
.from_err()
})
// We send back their nonce to check if the connection works.
.and_then(|(codec, mut context)| {
let remote_nonce = mem::replace(&mut context.remote_nonce, Vec::new());
codec.send(BytesMut::from(remote_nonce))
.map(|s| (s, context))
.from_err()
})
// Check that the received nonce is correct.
.and_then(|(codec, context)| {
codec.into_future()
.map_err(|(e, _)| e)
.and_then(move |(nonce, rest)| {
match nonce {
Some(ref n) if n == &context.local_nonce => {
Ok((rest, context.remote_public_key))
},
None => {
Err(IoError::new(IoErrorKind::BrokenPipe, "unexpected eof").into())
},
_ => Err(SecioError::NonceVerificationFailed)
}
})
});
// Check that the received nonce is correct.
.and_then(|(codec, context)| {
codec.into_future()
.map_err(|(e, _)| e)
.and_then(move |(nonce, rest)| {
match nonce {
Some(ref n) if n == &context.local_nonce => {
Ok((rest, context.remote_public_key))
},
None => {
Err(IoError::new(IoErrorKind::BrokenPipe, "unexpected eof").into())
},
_ => Err(SecioError::NonceVerificationFailed)
}
})
});
Box::new(future)
}
@ -444,8 +444,11 @@ fn stretch_key(key: &SigningKey, result: &mut [u8]) {
#[cfg(test)]
mod tests {
use super::handshake;
use super::stretch_key;
use futures::Future;
use futures::Stream;
use ring::digest::SHA256;
use ring::hmac::SigningKey;
use ring::signature::RSAKeyPair;
use std::sync::Arc;
use tokio_core::net::TcpListener;
@ -473,9 +476,9 @@ mod tests {
let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming()
.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(connec, _)| {
.into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(connec, _)| {
handshake(connec.unwrap().0, public_key1, private_key1)
});
@ -485,4 +488,265 @@ mod tests {
core.run(server.join(client)).unwrap();
}
#[test]
fn stretch() {
let mut output = [0u8; 32];
let key1 = SigningKey::new(&SHA256, &[]);
stretch_key(&key1, &mut output);
assert_eq!(
&output,
&[
103,
144,
60,
199,
85,
145,
239,
71,
79,
198,
85,
164,
32,
53,
143,
205,
50,
48,
153,
10,
37,
32,
85,
1,
226,
61,
193,
1,
154,
120,
207,
80,
]
);
let key2 = SigningKey::new(
&SHA256,
&[
157,
166,
80,
144,
77,
193,
198,
6,
23,
220,
87,
220,
191,
72,
168,
197,
54,
33,
219,
225,
84,
156,
165,
37,
149,
224,
244,
32,
170,
79,
125,
35,
171,
26,
178,
176,
92,
168,
22,
27,
205,
44,
229,
61,
152,
21,
222,
81,
241,
81,
116,
236,
74,
166,
89,
145,
5,
162,
108,
230,
55,
54,
9,
17,
],
);
stretch_key(&key2, &mut output);
assert_eq!(
&output,
&[
39,
151,
182,
63,
180,
175,
224,
139,
42,
131,
130,
116,
55,
146,
62,
31,
157,
95,
217,
15,
73,
81,
10,
83,
243,
141,
64,
227,
103,
144,
99,
121,
]
);
let key3 = SigningKey::new(
&SHA256,
&[
98,
219,
94,
104,
97,
70,
139,
13,
185,
110,
56,
36,
66,
3,
80,
224,
32,
205,
102,
170,
59,
32,
140,
245,
86,
102,
231,
68,
85,
249,
227,
243,
57,
53,
171,
36,
62,
225,
178,
74,
89,
142,
151,
94,
183,
231,
208,
166,
244,
130,
130,
209,
248,
65,
19,
48,
127,
127,
55,
82,
117,
154,
124,
108,
],
);
stretch_key(&key3, &mut output);
assert_eq!(
&output,
&[
28,
39,
158,
206,
164,
16,
211,
194,
99,
43,
208,
36,
24,
141,
90,
93,
157,
236,
238,
111,
170,
0,
60,
11,
49,
174,
177,
121,
30,
12,
182,
25,
]
);
}
}

View File

@ -64,7 +64,7 @@ pub struct SecioMiddleware<S> {
}
impl<S> SecioMiddleware<S>
where S: AsyncRead + AsyncWrite
where S: AsyncRead + AsyncWrite
{
/// Attempts to perform a handshake on the given socket.
///
@ -98,7 +98,7 @@ impl<S> SecioMiddleware<S>
}
impl<S> Sink for SecioMiddleware<S>
where S: AsyncRead + AsyncWrite
where S: AsyncRead + AsyncWrite
{
type SinkItem = BytesMut;
type SinkError = IoError;
@ -115,7 +115,7 @@ impl<S> Sink for SecioMiddleware<S>
}
impl<S> Stream for SecioMiddleware<S>
where S: AsyncRead + AsyncWrite
where S: AsyncRead + AsyncWrite
{
type Item = Vec<u8>;
type Error = SecioError;

View File

@ -14,4 +14,4 @@ format_strings=false
hard_tabs=true
wrap_match_arms=false
error_on_line_overflow=false
where_style="Legacy"
where_style="Legacy"