2017-10-30 10:22:38 +01:00
|
|
|
// Copyright 2017 Parity Technologies (UK) Ltd.
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
|
|
// to deal in the Software without restriction, including without limitation
|
|
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
//! Individual messages encoding and decoding. Use this after the algorithms have been
|
|
|
|
//! successfully negotiated.
|
|
|
|
|
|
|
|
use self::decode::DecoderMiddleware;
|
|
|
|
use self::encode::EncoderMiddleware;
|
|
|
|
|
2018-09-17 10:05:37 +02:00
|
|
|
use aes_ctr::stream_cipher::StreamCipherCore;
|
2018-10-01 15:42:40 +02:00
|
|
|
use algo_support::Digest;
|
|
|
|
use hmac::{self, Mac};
|
|
|
|
use sha2::{Sha256, Sha512};
|
2017-10-30 10:22:38 +01:00
|
|
|
use tokio_io::codec::length_delimited;
|
2018-05-14 15:55:16 +02:00
|
|
|
use tokio_io::{AsyncRead, AsyncWrite};
|
2017-10-30 10:22:38 +01:00
|
|
|
|
|
|
|
mod decode;
|
|
|
|
mod encode;
|
|
|
|
|
|
|
|
/// Type returned by `full_codec`.
|
|
|
|
pub type FullCodec<S> = DecoderMiddleware<EncoderMiddleware<length_delimited::Framed<S>>>;
|
|
|
|
|
2018-09-17 10:05:37 +02:00
|
|
|
pub type StreamCipher = Box<dyn StreamCipherCore + Send>;
|
2018-08-10 18:27:20 +02:00
|
|
|
|
2018-10-01 15:42:40 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum Hmac {
|
|
|
|
Sha256(hmac::Hmac<Sha256>),
|
|
|
|
Sha512(hmac::Hmac<Sha512>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Hmac {
|
|
|
|
/// Returns the size of the hash in bytes.
|
|
|
|
#[inline]
|
|
|
|
pub fn num_bytes(&self) -> usize {
|
|
|
|
match *self {
|
|
|
|
Hmac::Sha256(_) => 32,
|
|
|
|
Hmac::Sha512(_) => 64,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Builds a `Hmac` from an algorithm and key.
|
|
|
|
pub fn from_key(algorithm: Digest, key: &[u8]) -> Self {
|
|
|
|
// TODO: it would be nice to tweak the hmac crate to add an equivalent to new_varkey that
|
|
|
|
// never errors
|
|
|
|
match algorithm {
|
|
|
|
Digest::Sha256 => Hmac::Sha256(Mac::new_varkey(key)
|
|
|
|
.expect("Hmac::new_varkey accepts any key length")),
|
|
|
|
Digest::Sha512 => Hmac::Sha512(Mac::new_varkey(key)
|
|
|
|
.expect("Hmac::new_varkey accepts any key length")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Signs the data.
|
|
|
|
// TODO: better return type?
|
|
|
|
pub fn sign(&mut self, crypted_data: &[u8]) -> Vec<u8> {
|
|
|
|
match *self {
|
|
|
|
Hmac::Sha256(ref mut hmac) => {
|
|
|
|
hmac.input(crypted_data);
|
|
|
|
hmac.result().code().to_vec()
|
|
|
|
},
|
|
|
|
Hmac::Sha512(ref mut hmac) => {
|
|
|
|
hmac.input(crypted_data);
|
|
|
|
hmac.result().code().to_vec()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Verifies that the data matches the expected hash.
|
|
|
|
// TODO: better error?
|
|
|
|
pub fn verify(&mut self, crypted_data: &[u8], expected_hash: &[u8]) -> Result<(), ()> {
|
|
|
|
match *self {
|
|
|
|
Hmac::Sha256(ref mut hmac) => {
|
|
|
|
hmac.input(crypted_data);
|
|
|
|
hmac.verify(expected_hash).map_err(|_| ())
|
|
|
|
},
|
|
|
|
Hmac::Sha512(ref mut hmac) => {
|
|
|
|
hmac.input(crypted_data);
|
|
|
|
hmac.verify(expected_hash).map_err(|_| ())
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-10 18:27:20 +02:00
|
|
|
|
2017-10-30 10:22:38 +01:00
|
|
|
/// Takes control of `socket`. Returns an object that implements `future::Sink` and
|
|
|
|
/// `future::Stream`. The `Stream` and `Sink` produce and accept `BytesMut` objects.
|
|
|
|
///
|
|
|
|
/// The conversion between the stream/sink items and the socket is done with the given cipher and
|
|
|
|
/// hash algorithm (which are generally decided during the handshake).
|
|
|
|
pub fn full_codec<S>(
|
2018-03-07 16:20:55 +01:00
|
|
|
socket: length_delimited::Framed<S>,
|
2018-08-15 17:00:57 +02:00
|
|
|
cipher_encoding: StreamCipher,
|
2018-10-01 15:42:40 +02:00
|
|
|
encoding_hmac: Hmac,
|
2018-08-15 17:00:57 +02:00
|
|
|
cipher_decoder: StreamCipher,
|
2018-10-01 15:42:40 +02:00
|
|
|
decoding_hmac: Hmac,
|
2018-11-21 10:39:48 +01:00
|
|
|
remote_nonce: Vec<u8>
|
2017-10-30 10:22:38 +01:00
|
|
|
) -> FullCodec<S>
|
2018-03-07 16:20:55 +01:00
|
|
|
where
|
|
|
|
S: AsyncRead + AsyncWrite,
|
2017-10-30 10:22:38 +01:00
|
|
|
{
|
2018-03-07 16:20:55 +01:00
|
|
|
let encoder = EncoderMiddleware::new(socket, cipher_encoding, encoding_hmac);
|
2018-11-21 10:39:48 +01:00
|
|
|
DecoderMiddleware::new(encoder, cipher_decoder, decoding_hmac, remote_nonce)
|
2017-10-30 10:22:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2018-10-25 05:26:37 -04:00
|
|
|
extern crate tokio;
|
2018-07-16 12:15:27 +02:00
|
|
|
extern crate tokio_tcp;
|
2018-10-25 05:26:37 -04:00
|
|
|
use self::tokio::runtime::current_thread::Runtime;
|
2018-07-16 12:15:27 +02:00
|
|
|
use self::tokio_tcp::TcpListener;
|
|
|
|
use self::tokio_tcp::TcpStream;
|
2018-09-05 02:15:16 +02:00
|
|
|
use stream_cipher::{ctr, Cipher};
|
2018-07-11 11:14:40 +02:00
|
|
|
use super::full_codec;
|
2018-03-07 16:20:55 +01:00
|
|
|
use super::DecoderMiddleware;
|
|
|
|
use super::EncoderMiddleware;
|
2018-10-01 15:42:40 +02:00
|
|
|
use super::Hmac;
|
|
|
|
use algo_support::Digest;
|
2018-03-07 16:20:55 +01:00
|
|
|
use bytes::BytesMut;
|
|
|
|
use error::SecioError;
|
|
|
|
use futures::sync::mpsc::channel;
|
2018-11-21 10:39:48 +01:00
|
|
|
use futures::{Future, Sink, Stream, stream};
|
2018-03-07 16:20:55 +01:00
|
|
|
use rand;
|
|
|
|
use std::io::Error as IoError;
|
|
|
|
use tokio_io::codec::length_delimited::Framed;
|
|
|
|
|
2018-09-17 10:05:37 +02:00
|
|
|
const NULL_IV : [u8; 16] = [0;16];
|
2018-08-10 18:27:20 +02:00
|
|
|
|
2018-03-07 16:20:55 +01:00
|
|
|
#[test]
|
|
|
|
fn raw_encode_then_decode() {
|
|
|
|
let (data_tx, data_rx) = channel::<BytesMut>(256);
|
|
|
|
let data_tx = data_tx.sink_map_err::<_, IoError>(|_| panic!());
|
|
|
|
let data_rx = data_rx.map_err::<IoError, _>(|_| panic!());
|
|
|
|
|
|
|
|
let cipher_key: [u8; 32] = rand::random();
|
|
|
|
let hmac_key: [u8; 32] = rand::random();
|
|
|
|
|
2018-09-17 10:05:37 +02:00
|
|
|
|
2018-03-07 16:20:55 +01:00
|
|
|
let encoder = EncoderMiddleware::new(
|
|
|
|
data_tx,
|
2018-09-05 02:15:16 +02:00
|
|
|
ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]),
|
2018-10-01 15:42:40 +02:00
|
|
|
Hmac::from_key(Digest::Sha256, &hmac_key),
|
2018-03-07 16:20:55 +01:00
|
|
|
);
|
|
|
|
let decoder = DecoderMiddleware::new(
|
|
|
|
data_rx,
|
2018-09-05 02:15:16 +02:00
|
|
|
ctr(Cipher::Aes256, &cipher_key, &NULL_IV[..]),
|
2018-10-01 15:42:40 +02:00
|
|
|
Hmac::from_key(Digest::Sha256, &hmac_key),
|
2018-11-21 10:39:48 +01:00
|
|
|
Vec::new()
|
2018-03-07 16:20:55 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
let data = b"hello world";
|
|
|
|
|
|
|
|
let data_sent = encoder.send(BytesMut::from(data.to_vec())).from_err();
|
|
|
|
let data_received = decoder.into_future().map(|(n, _)| n).map_err(|(e, _)| e);
|
2018-10-25 05:26:37 -04:00
|
|
|
let mut rt = Runtime::new().unwrap();
|
2018-03-07 16:20:55 +01:00
|
|
|
|
2018-10-25 05:26:37 -04:00
|
|
|
let (_, decoded) = rt.block_on(data_sent.join(data_received))
|
2018-03-07 16:20:55 +01:00
|
|
|
.map_err(|_| ())
|
|
|
|
.unwrap();
|
2018-09-17 10:05:37 +02:00
|
|
|
assert_eq!(&decoded.unwrap()[..], &data[..]);
|
2018-03-07 16:20:55 +01:00
|
|
|
}
|
|
|
|
|
2018-09-05 02:15:16 +02:00
|
|
|
fn full_codec_encode_then_decode(cipher: Cipher) {
|
2018-03-07 16:20:55 +01:00
|
|
|
let cipher_key: [u8; 32] = rand::random();
|
|
|
|
let cipher_key_clone = cipher_key.clone();
|
2018-09-05 02:15:16 +02:00
|
|
|
let key_size = cipher.key_size();
|
|
|
|
let hmac_key: [u8; 16] = rand::random();
|
2018-03-07 16:20:55 +01:00
|
|
|
let hmac_key_clone = hmac_key.clone();
|
|
|
|
let data = b"hello world";
|
|
|
|
let data_clone = data.clone();
|
2018-11-21 10:39:48 +01:00
|
|
|
let nonce = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
2018-03-07 16:20:55 +01:00
|
|
|
|
2018-07-16 12:15:27 +02:00
|
|
|
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
|
2018-03-07 16:20:55 +01:00
|
|
|
let listener_addr = listener.local_addr().unwrap();
|
|
|
|
|
2018-11-21 10:39:48 +01:00
|
|
|
let nonce2 = nonce.clone();
|
|
|
|
let server = listener.incoming()
|
|
|
|
.into_future()
|
|
|
|
.map_err(|(e, _)| e)
|
|
|
|
.map(move |(connec, _)| {
|
2018-03-07 16:20:55 +01:00
|
|
|
full_codec(
|
2018-11-21 10:39:48 +01:00
|
|
|
Framed::new(connec.unwrap()),
|
2018-09-05 02:15:16 +02:00
|
|
|
ctr(cipher, &cipher_key[..key_size], &NULL_IV[..]),
|
2018-10-01 15:42:40 +02:00
|
|
|
Hmac::from_key(Digest::Sha256, &hmac_key),
|
2018-09-05 02:15:16 +02:00
|
|
|
ctr(cipher, &cipher_key[..key_size], &NULL_IV[..]),
|
2018-10-01 15:42:40 +02:00
|
|
|
Hmac::from_key(Digest::Sha256, &hmac_key),
|
2018-11-21 10:39:48 +01:00
|
|
|
nonce2
|
2018-03-07 16:20:55 +01:00
|
|
|
)
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2018-07-16 12:15:27 +02:00
|
|
|
let client = TcpStream::connect(&listener_addr)
|
2018-03-07 16:20:55 +01:00
|
|
|
.map_err(|e| e.into())
|
|
|
|
.map(move |stream| {
|
|
|
|
full_codec(
|
2018-11-21 10:39:48 +01:00
|
|
|
Framed::new(stream),
|
2018-09-05 02:15:16 +02:00
|
|
|
ctr(cipher, &cipher_key_clone[..key_size], &NULL_IV[..]),
|
2018-10-01 15:42:40 +02:00
|
|
|
Hmac::from_key(Digest::Sha256, &hmac_key_clone),
|
2018-09-05 02:15:16 +02:00
|
|
|
ctr(cipher, &cipher_key_clone[..key_size], &NULL_IV[..]),
|
2018-10-01 15:42:40 +02:00
|
|
|
Hmac::from_key(Digest::Sha256, &hmac_key_clone),
|
2018-11-21 10:39:48 +01:00
|
|
|
Vec::new()
|
2018-03-07 16:20:55 +01:00
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
let fin = server
|
|
|
|
.join(client)
|
|
|
|
.from_err::<SecioError>()
|
|
|
|
.and_then(|(server, client)| {
|
|
|
|
client
|
2018-11-21 10:39:48 +01:00
|
|
|
.send_all(stream::iter_ok::<_, IoError>(vec![nonce.into(), data_clone[..].into()]))
|
2018-03-07 16:20:55 +01:00
|
|
|
.map(move |_| server)
|
|
|
|
.from_err()
|
|
|
|
})
|
2018-11-21 10:39:48 +01:00
|
|
|
.and_then(|server| server.concat2().from_err());
|
2018-03-07 16:20:55 +01:00
|
|
|
|
2018-10-25 05:26:37 -04:00
|
|
|
let mut rt = Runtime::new().unwrap();
|
|
|
|
let received = rt.block_on(fin).unwrap();
|
2018-03-07 16:20:55 +01:00
|
|
|
assert_eq!(received, data);
|
|
|
|
}
|
2018-09-05 02:15:16 +02:00
|
|
|
|
|
|
|
#[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);
|
|
|
|
}
|
2018-09-12 09:10:05 +02:00
|
|
|
|
2018-09-17 10:05:37 +02:00
|
|
|
#[test]
|
|
|
|
fn full_codec_encode_then_decode_twofish() {
|
|
|
|
full_codec_encode_then_decode(Cipher::TwofishCtr);
|
|
|
|
}
|
|
|
|
|
2018-09-12 09:10:05 +02:00
|
|
|
#[test]
|
|
|
|
fn full_codec_encode_then_decode_null() {
|
|
|
|
full_codec_encode_then_decode(Cipher::Null);
|
|
|
|
}
|
2017-10-30 10:22:38 +01:00
|
|
|
}
|