[devp2p] Don't use rust-crypto
(#10714)
* Run cargo fix * Optimize imports * compiles * cleanup * Use Secret to store mac-key Truncate payload properly * cleanup * Reorg imports * brwchk hand waving * Review feedback * whitespace * error chain is dead
This commit is contained in:
parent
35c607f6be
commit
2d693be735
@ -27,14 +27,11 @@ use mio::{PollOpt, Ready, Token};
|
|||||||
use mio::deprecated::{EventLoop, Handler, TryRead, TryWrite};
|
use mio::deprecated::{EventLoop, Handler, TryRead, TryWrite};
|
||||||
use mio::tcp::*;
|
use mio::tcp::*;
|
||||||
use parity_bytes::*;
|
use parity_bytes::*;
|
||||||
use rcrypto::aessafe::*;
|
use crypto::aes::{AesCtr256, AesEcb256};
|
||||||
use rcrypto::blockmodes::*;
|
|
||||||
use rcrypto::buffer::*;
|
|
||||||
use rcrypto::symmetriccipher::*;
|
|
||||||
use rlp::{Rlp, RlpStream};
|
use rlp::{Rlp, RlpStream};
|
||||||
use tiny_keccak::Keccak;
|
use tiny_keccak::Keccak;
|
||||||
|
|
||||||
use ethkey::crypto;
|
use ethkey::{crypto, Secret};
|
||||||
use handshake::Handshake;
|
use handshake::Handshake;
|
||||||
use io::{IoContext, StreamToken};
|
use io::{IoContext, StreamToken};
|
||||||
use network::Error;
|
use network::Error;
|
||||||
@ -279,11 +276,11 @@ pub struct EncryptedConnection {
|
|||||||
/// Underlying tcp connection
|
/// Underlying tcp connection
|
||||||
pub connection: Connection,
|
pub connection: Connection,
|
||||||
/// Egress data encryptor
|
/// Egress data encryptor
|
||||||
encoder: CtrMode<AesSafe256Encryptor>,
|
encoder: AesCtr256,
|
||||||
/// Ingress data decryptor
|
/// Ingress data decryptor
|
||||||
decoder: CtrMode<AesSafe256Encryptor>,
|
decoder: AesCtr256,
|
||||||
/// Ingress data decryptor
|
/// Ingress data decryptor
|
||||||
mac_encoder: EcbEncryptor<AesSafe256Encryptor, EncPadding<NoPadding>>,
|
mac_encoder_key: Secret,
|
||||||
/// MAC for egress data
|
/// MAC for egress data
|
||||||
egress_mac: Keccak,
|
egress_mac: Keccak,
|
||||||
/// MAC for ingress data
|
/// MAC for ingress data
|
||||||
@ -296,6 +293,7 @@ pub struct EncryptedConnection {
|
|||||||
payload_len: usize,
|
payload_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NULL_IV : [u8; 16] = [0;16];
|
||||||
impl EncryptedConnection {
|
impl EncryptedConnection {
|
||||||
/// Create an encrypted connection out of the handshake.
|
/// Create an encrypted connection out of the handshake.
|
||||||
pub fn new(handshake: &mut Handshake) -> Result<EncryptedConnection, Error> {
|
pub fn new(handshake: &mut Handshake) -> Result<EncryptedConnection, Error> {
|
||||||
@ -317,14 +315,15 @@ impl EncryptedConnection {
|
|||||||
let key_material_keccak = keccak(&key_material);
|
let key_material_keccak = keccak(&key_material);
|
||||||
(&mut key_material[32..64]).copy_from_slice(key_material_keccak.as_bytes());
|
(&mut key_material[32..64]).copy_from_slice(key_material_keccak.as_bytes());
|
||||||
|
|
||||||
let iv = vec![0u8; 16];
|
// Using a 0 IV with CTR is fine as long as the same IV is never reused with the same key.
|
||||||
let encoder = CtrMode::new(AesSafe256Encryptor::new(&key_material[32..64]), iv);
|
// This is the case here: ecdh creates a new secret which will be the symmetric key used
|
||||||
let iv = vec![0u8; 16];
|
// only for this session the 0 IV is only use once with this secret, so we are in the case
|
||||||
let decoder = CtrMode::new(AesSafe256Encryptor::new(&key_material[32..64]), iv);
|
// of same IV use for different key.
|
||||||
|
let encoder = AesCtr256::new(&key_material[32..64], &NULL_IV)?;
|
||||||
|
let decoder = AesCtr256::new(&key_material[32..64], &NULL_IV)?;
|
||||||
let key_material_keccak = keccak(&key_material);
|
let key_material_keccak = keccak(&key_material);
|
||||||
(&mut key_material[32..64]).copy_from_slice(key_material_keccak.as_bytes());
|
(&mut key_material[32..64]).copy_from_slice(key_material_keccak.as_bytes());
|
||||||
let mac_encoder = EcbEncryptor::new(AesSafe256Encryptor::new(&key_material[32..64]), NoPadding);
|
let mac_encoder_key: Secret = Secret::from_slice(&key_material[32..64]).expect("can create Secret from 32 bytes; qed");
|
||||||
|
|
||||||
let mut egress_mac = Keccak::new_keccak256();
|
let mut egress_mac = Keccak::new_keccak256();
|
||||||
let mut mac_material = H256::from_slice(&key_material[32..64]) ^ handshake.remote_nonce;
|
let mut mac_material = H256::from_slice(&key_material[32..64]) ^ handshake.remote_nonce;
|
||||||
@ -342,7 +341,7 @@ impl EncryptedConnection {
|
|||||||
connection,
|
connection,
|
||||||
encoder,
|
encoder,
|
||||||
decoder,
|
decoder,
|
||||||
mac_encoder,
|
mac_encoder_key,
|
||||||
egress_mac,
|
egress_mac,
|
||||||
ingress_mac,
|
ingress_mac,
|
||||||
read_state: EncryptedConnectionState::Header,
|
read_state: EncryptedConnectionState::Header,
|
||||||
@ -355,56 +354,55 @@ impl EncryptedConnection {
|
|||||||
|
|
||||||
/// Send a packet
|
/// Send a packet
|
||||||
pub fn send_packet<Message>(&mut self, io: &IoContext<Message>, payload: &[u8]) -> Result<(), Error> where Message: Send + Clone + Sync + 'static {
|
pub fn send_packet<Message>(&mut self, io: &IoContext<Message>, payload: &[u8]) -> Result<(), Error> where Message: Send + Clone + Sync + 'static {
|
||||||
|
const HEADER_LEN: usize = 16;
|
||||||
let mut header = RlpStream::new();
|
let mut header = RlpStream::new();
|
||||||
let len = payload.len();
|
let len = payload.len();
|
||||||
if len > MAX_PAYLOAD_SIZE {
|
if len > MAX_PAYLOAD_SIZE {
|
||||||
return Err(Error::OversizedPacket);
|
return Err(Error::OversizedPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
header.append_raw(&[(len >> 16) as u8, (len >> 8) as u8, len as u8], 1);
|
header.append_raw(&[(len >> 16) as u8, (len >> 8) as u8, len as u8], 1);
|
||||||
header.append_raw(&[0xc2u8, 0x80u8, 0x80u8], 1);
|
header.append_raw(&[0xc2u8, 0x80u8, 0x80u8], 1);
|
||||||
//TODO: get rid of vectors here
|
let padding = (16 - (len % 16)) % 16;
|
||||||
let mut header = header.out();
|
|
||||||
let padding = (16 - (payload.len() % 16)) % 16;
|
|
||||||
header.resize(16, 0u8);
|
|
||||||
|
|
||||||
let mut packet = vec![0u8; 32 + payload.len() + padding + 16];
|
let mut packet = vec![0u8; 16 + 16 + len + padding + 16];
|
||||||
self.encoder.encrypt(&mut RefReadBuffer::new(&header), &mut RefWriteBuffer::new(&mut packet), false).expect("Invalid length or padding");
|
let mut header = header.out();
|
||||||
EncryptedConnection::update_mac(&mut self.egress_mac, &mut self.mac_encoder, &packet[0..16]);
|
header.resize(HEADER_LEN, 0u8);
|
||||||
self.egress_mac.clone().finalize(&mut packet[16..32]);
|
&mut packet[..HEADER_LEN].copy_from_slice(&mut header);
|
||||||
self.encoder.encrypt(&mut RefReadBuffer::new(payload), &mut RefWriteBuffer::new(&mut packet[32..(32 + len)]), padding == 0).expect("Invalid length or padding");
|
self.encoder.encrypt(&mut packet[..HEADER_LEN])?;
|
||||||
|
EncryptedConnection::update_mac(&mut self.egress_mac, &self.mac_encoder_key, &packet[..HEADER_LEN])?;
|
||||||
|
self.egress_mac.clone().finalize(&mut packet[HEADER_LEN..32]);
|
||||||
|
&mut packet[32..32 + len].copy_from_slice(payload);
|
||||||
|
self.encoder.encrypt(&mut packet[32..32 + len])?;
|
||||||
if padding != 0 {
|
if padding != 0 {
|
||||||
let pad = [0u8; 16];
|
self.encoder.encrypt(&mut packet[(32 + len)..(32 + len + padding)])?;
|
||||||
self.encoder.encrypt(&mut RefReadBuffer::new(&pad[0..padding]), &mut RefWriteBuffer::new(&mut packet[(32 + len)..(32 + len + padding)]), true).expect("Invalid length or padding");
|
|
||||||
}
|
}
|
||||||
self.egress_mac.update(&packet[32..(32 + len + padding)]);
|
self.egress_mac.update(&packet[32..(32 + len + padding)]);
|
||||||
EncryptedConnection::update_mac(&mut self.egress_mac, &mut self.mac_encoder, &[0u8; 0]);
|
EncryptedConnection::update_mac(&mut self.egress_mac, &self.mac_encoder_key, &[0u8; 0])?;
|
||||||
self.egress_mac.clone().finalize(&mut packet[(32 + len + padding)..]);
|
self.egress_mac.clone().finalize(&mut packet[(32 + len + padding)..]);
|
||||||
self.connection.send(io, packet);
|
self.connection.send(io, packet);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt and authenticate an incoming packet header. Prepare for receiving payload.
|
/// Decrypt and authenticate an incoming packet header. Prepare for receiving payload.
|
||||||
fn read_header(&mut self, header: &[u8]) -> Result<(), Error> {
|
fn read_header(&mut self, mut header: Bytes) -> Result<(), Error> {
|
||||||
if header.len() != ENCRYPTED_HEADER_LEN {
|
if header.len() != ENCRYPTED_HEADER_LEN {
|
||||||
return Err(Error::Auth);
|
return Err(Error::Auth);
|
||||||
}
|
}
|
||||||
EncryptedConnection::update_mac(&mut self.ingress_mac, &mut self.mac_encoder, &header[0..16]);
|
EncryptedConnection::update_mac(&mut self.ingress_mac, &self.mac_encoder_key, &header[0..16])?;
|
||||||
|
{
|
||||||
let mac = &header[16..];
|
let mac = &header[16..];
|
||||||
let mut expected = H256::zero();
|
let mut expected = H256::zero();
|
||||||
self.ingress_mac.clone().finalize(expected.as_bytes_mut());
|
self.ingress_mac.clone().finalize(expected.as_bytes_mut());
|
||||||
if mac != &expected[0..16] {
|
if mac != &expected[0..16] {
|
||||||
return Err(Error::Auth);
|
return Err(Error::Auth);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
self.decoder.decrypt(&mut header[..16])?;
|
||||||
|
|
||||||
let mut hdec = H128::default();
|
let length = ((((header[0] as u32) << 8) + (header[1] as u32)) << 8) + (header[2] as u32);
|
||||||
self.decoder.decrypt(
|
let header_rlp = Rlp::new(&header[3..6]);
|
||||||
&mut RefReadBuffer::new(&header[0..16]),
|
|
||||||
&mut RefWriteBuffer::new(hdec.as_bytes_mut()),
|
|
||||||
false,
|
|
||||||
).expect("Invalid length or padding");
|
|
||||||
|
|
||||||
let length = ((((hdec[0] as u32) << 8) + (hdec[1] as u32)) << 8) + (hdec[2] as u32);
|
|
||||||
let header_rlp = Rlp::new(&hdec[3..6]);
|
|
||||||
let protocol_id = header_rlp.val_at::<u16>(0)?;
|
let protocol_id = header_rlp.val_at::<u16>(0)?;
|
||||||
|
|
||||||
self.payload_len = length as usize;
|
self.payload_len = length as usize;
|
||||||
@ -413,50 +411,49 @@ impl EncryptedConnection {
|
|||||||
|
|
||||||
let padding = (16 - (length % 16)) % 16;
|
let padding = (16 - (length % 16)) % 16;
|
||||||
let full_length = length + padding + 16;
|
let full_length = length + padding + 16;
|
||||||
|
|
||||||
self.connection.expect(full_length as usize);
|
self.connection.expect(full_length as usize);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt and authenticate packet payload.
|
/// Decrypt and authenticate packet payload.
|
||||||
fn read_payload(&mut self, payload: &[u8]) -> Result<Packet, Error> {
|
fn read_payload(&mut self, mut payload: Bytes) -> Result<Packet, Error> {
|
||||||
let padding = (16 - (self.payload_len % 16)) % 16;
|
let padding = (16 - (self.payload_len % 16)) % 16;
|
||||||
let full_length = self.payload_len + padding + 16;
|
let full_length = self.payload_len + padding + 16;
|
||||||
if payload.len() != full_length {
|
if payload.len() != full_length {
|
||||||
return Err(Error::Auth);
|
return Err(Error::Auth);
|
||||||
}
|
}
|
||||||
self.ingress_mac.update(&payload[0..payload.len() - 16]);
|
self.ingress_mac.update(&payload[0..payload.len() - 16]);
|
||||||
EncryptedConnection::update_mac(&mut self.ingress_mac, &mut self.mac_encoder, &[0u8; 0]);
|
EncryptedConnection::update_mac(&mut self.ingress_mac, &self.mac_encoder_key, &[0u8; 0])?;
|
||||||
|
|
||||||
|
{
|
||||||
let mac = &payload[(payload.len() - 16)..];
|
let mac = &payload[(payload.len() - 16)..];
|
||||||
let mut expected = H128::default();
|
let mut expected = H128::default();
|
||||||
self.ingress_mac.clone().finalize(expected.as_bytes_mut());
|
self.ingress_mac.clone().finalize(expected.as_bytes_mut());
|
||||||
if mac != &expected[..] {
|
if mac != &expected[..] {
|
||||||
return Err(Error::Auth);
|
return Err(Error::Auth);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let mut packet = vec![0u8; self.payload_len];
|
self.decoder.decrypt(&mut payload[..self.payload_len + padding])?;
|
||||||
self.decoder.decrypt(&mut RefReadBuffer::new(&payload[0..self.payload_len]), &mut RefWriteBuffer::new(&mut packet), false).expect("Invalid length or padding");
|
payload.truncate(self.payload_len);
|
||||||
let mut pad_buf = [0u8; 16];
|
|
||||||
self.decoder.decrypt(&mut RefReadBuffer::new(&payload[self.payload_len..(payload.len() - 16)]), &mut RefWriteBuffer::new(&mut pad_buf), false).expect("Invalid length or padding");
|
|
||||||
Ok(Packet {
|
Ok(Packet {
|
||||||
protocol: self.protocol_id,
|
protocol: self.protocol_id,
|
||||||
data: packet
|
data: payload
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update MAC after reading or writing any data.
|
/// Update MAC after reading or writing any data.
|
||||||
fn update_mac(mac: &mut Keccak, mac_encoder: &mut EcbEncryptor<AesSafe256Encryptor, EncPadding<NoPadding>>, seed: &[u8]) {
|
fn update_mac(mac: &mut Keccak, mac_encoder_key: &Secret, seed: &[u8]) -> Result<(), Error> {
|
||||||
let mut prev = H128::default();
|
let mut prev = H128::default();
|
||||||
mac.clone().finalize(prev.as_bytes_mut());
|
mac.clone().finalize(prev.as_bytes_mut());
|
||||||
let mut enc = H128::default();
|
let mut enc = H128::default();
|
||||||
mac_encoder.encrypt(
|
&mut enc[..].copy_from_slice(prev.as_bytes());
|
||||||
&mut RefReadBuffer::new(prev.as_bytes()),
|
let mac_encoder = AesEcb256::new(mac_encoder_key.as_bytes())?;
|
||||||
&mut RefWriteBuffer::new(enc.as_bytes_mut()),
|
mac_encoder.encrypt(enc.as_bytes_mut())?;
|
||||||
true
|
|
||||||
).expect("Error updating MAC");
|
|
||||||
mac_encoder.reset();
|
|
||||||
|
|
||||||
enc = enc ^ if seed.is_empty() { prev } else { H128::from_slice(seed) };
|
enc = enc ^ if seed.is_empty() { prev } else { H128::from_slice(seed) };
|
||||||
mac.update(enc.as_bytes());
|
mac.update(enc.as_bytes());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Readable IO handler. Tracker receive status and returns decoded packet if available.
|
/// Readable IO handler. Tracker receive status and returns decoded packet if available.
|
||||||
@ -464,7 +461,7 @@ impl EncryptedConnection {
|
|||||||
io.clear_timer(self.connection.token)?;
|
io.clear_timer(self.connection.token)?;
|
||||||
if let EncryptedConnectionState::Header = self.read_state {
|
if let EncryptedConnectionState::Header = self.read_state {
|
||||||
if let Some(data) = self.connection.readable()? {
|
if let Some(data) = self.connection.readable()? {
|
||||||
self.read_header(&data)?;
|
self.read_header(data)?;
|
||||||
io.register_timer(self.connection.token, RECEIVE_PAYLOAD)?;
|
io.register_timer(self.connection.token, RECEIVE_PAYLOAD)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -473,7 +470,7 @@ impl EncryptedConnection {
|
|||||||
Some(data) => {
|
Some(data) => {
|
||||||
self.read_state = EncryptedConnectionState::Header;
|
self.read_state = EncryptedConnectionState::Header;
|
||||||
self.connection.expect(ENCRYPTED_HEADER_LEN);
|
self.connection.expect(ENCRYPTED_HEADER_LEN);
|
||||||
Ok(Some(self.read_payload(&data)?))
|
Ok(Some(self.read_payload(data)?))
|
||||||
},
|
},
|
||||||
None => Ok(None)
|
None => Ok(None)
|
||||||
}
|
}
|
||||||
@ -489,28 +486,6 @@ impl EncryptedConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn test_encryption() {
|
|
||||||
use ethereum_types::{H256, H128};
|
|
||||||
use std::str::FromStr;
|
|
||||||
let key = H256::from_str("2212767d793a7a3d66f869ae324dd11bd17044b82c9f463b8a541a4d089efec5").unwrap();
|
|
||||||
let before = H128::from_str("12532abaec065082a3cf1da7d0136f15").unwrap();
|
|
||||||
let before2 = H128::from_str("7e99f682356fdfbc6b67a9562787b18a").unwrap();
|
|
||||||
let after = H128::from_str("89464c6b04e7c99e555c81d3f7266a05").unwrap();
|
|
||||||
let after2 = H128::from_str("85c070030589ef9c7a2879b3a8489316").unwrap();
|
|
||||||
|
|
||||||
let mut got = H128::zero();
|
|
||||||
|
|
||||||
let mut encoder = EcbEncryptor::new(AesSafe256Encryptor::new(key.as_bytes()), NoPadding);
|
|
||||||
encoder.encrypt(&mut RefReadBuffer::new(before.as_bytes()), &mut RefWriteBuffer::new(got.as_bytes_mut()), true).unwrap();
|
|
||||||
encoder.reset();
|
|
||||||
assert_eq!(got, after);
|
|
||||||
got = H128::zero();
|
|
||||||
encoder.encrypt(&mut RefReadBuffer::new(before2.as_bytes()), &mut RefWriteBuffer::new(got.as_bytes_mut()), true).unwrap();
|
|
||||||
encoder.reset();
|
|
||||||
assert_eq!(got, after2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
@ -665,6 +640,30 @@ mod tests {
|
|||||||
IoContext::new(IoChannel::disconnected(), 0)
|
IoContext::new(IoChannel::disconnected(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_encryption() {
|
||||||
|
use ethereum_types::{H256, H128};
|
||||||
|
use std::str::FromStr;
|
||||||
|
let key = H256::from_str("2212767d793a7a3d66f869ae324dd11bd17044b82c9f463b8a541a4d089efec5").unwrap();
|
||||||
|
let before = H128::from_str("12532abaec065082a3cf1da7d0136f15").unwrap();
|
||||||
|
let before2 = H128::from_str("7e99f682356fdfbc6b67a9562787b18a").unwrap();
|
||||||
|
let after = H128::from_str("89464c6b04e7c99e555c81d3f7266a05").unwrap();
|
||||||
|
let after2 = H128::from_str("85c070030589ef9c7a2879b3a8489316").unwrap();
|
||||||
|
|
||||||
|
let mut got = H128::default();
|
||||||
|
|
||||||
|
let encoder = AesEcb256::new(key.as_bytes()).unwrap();
|
||||||
|
got.as_bytes_mut().copy_from_slice(before.as_bytes());
|
||||||
|
encoder.encrypt(got.as_bytes_mut()).unwrap();
|
||||||
|
assert_eq!(got, after);
|
||||||
|
|
||||||
|
let encoder = AesEcb256::new(key.as_bytes()).unwrap();
|
||||||
|
got = H128::default();
|
||||||
|
got.as_bytes_mut().copy_from_slice(&before2.as_bytes());
|
||||||
|
encoder.encrypt(got.as_bytes_mut()).unwrap();
|
||||||
|
assert_eq!(got, after2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connection_expect() {
|
fn connection_expect() {
|
||||||
let mut connection = TestConnection::new();
|
let mut connection = TestConnection::new();
|
||||||
|
@ -65,7 +65,6 @@ extern crate ansi_term;
|
|||||||
#[cfg(test)] #[macro_use]
|
#[cfg(test)] #[macro_use]
|
||||||
extern crate assert_matches;
|
extern crate assert_matches;
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
extern crate crypto as rcrypto;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate ethcore_io as io;
|
extern crate ethcore_io as io;
|
||||||
|
Loading…
Reference in New Issue
Block a user