udp relay supports AEAD ciphers

This commit is contained in:
Y. T. Chung
2017-02-16 21:38:35 +08:00
parent 83f9302548
commit 0cb7bd428b
4 changed files with 135 additions and 161 deletions

View File

@@ -0,0 +1,113 @@
//! Crypto protocol for ShadowSocks UDP
//!
//! Payload with stream cipher
//! ```plain
//! +-------+----------+
//! | IV | Payload |
//! +-------+----------+
//! | Fixed | Variable |
//! +-------+----------+
//! ```
//!
//! Payload with AEAD cipher
//!
//! ```plain
//! UDP (after encryption, *ciphertext*)
//! +--------+-----------+-----------+
//! | NONCE | *Data* | Data_TAG |
//! +--------+-----------+-----------+
//! | Fixed | Variable | Fixed |
//! +--------+-----------+-----------+
//! ```
use std::io;
use crypto::{self, CipherType, CipherCategory, CryptoMode};
use crypto::StreamCipher;
/// Encrypt payload into ShadowSocks UDP encrypted packet
pub fn encrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
match t.category() {
CipherCategory::Stream => encrypt_payload_stream(t, key, payload),
CipherCategory::Aead => encrypt_payload_aead(t, key, payload),
}
}
fn encrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
let mut iv = t.gen_init_vec();
let mut cipher = crypto::new_stream(t, key, &iv, CryptoMode::Encrypt);
let mut send_payload = Vec::with_capacity(iv.len() + payload.len());
send_payload.append(&mut iv);
try!(cipher.update(&payload[..], &mut send_payload));
try!(cipher.finalize(&mut send_payload));
Ok(send_payload)
}
fn encrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
let mut iv = t.gen_init_vec();
let tag_size = t.tag_size();
let mut cipher = crypto::new_aead_encryptor(t, key, &iv);
let mut send_payload = Vec::with_capacity(iv.len() + payload.len() + tag_size);
send_payload.append(&mut iv);
let start_pos = send_payload.len();
send_payload.resize(start_pos + payload.len(), 0);
let mut tag_buf = vec![0u8; tag_size];
cipher.encrypt(payload, &mut send_payload[start_pos..], &mut tag_buf);
send_payload.append(&mut tag_buf);
Ok(send_payload)
}
/// Decrypt payload from ShadowSocks UDP encrypted packet
pub fn decrypt_payload(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
match t.category() {
CipherCategory::Stream => decrypt_payload_stream(t, key, payload),
CipherCategory::Aead => decrypt_payload_aead(t, key, payload),
}
}
fn decrypt_payload_stream(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
let iv_size = t.iv_size();
if payload.len() < iv_size {
let err = io::Error::new(io::ErrorKind::Other, "udp packet too short");
return Err(err);
}
let iv = &payload[..iv_size];
let data = &payload[iv_size..];
let mut cipher = crypto::new_stream(t, key, iv, CryptoMode::Decrypt);
let mut recv_payload = Vec::with_capacity(data.len());
try!(cipher.update(data, &mut recv_payload));
try!(cipher.finalize(&mut recv_payload));
Ok(recv_payload)
}
fn decrypt_payload_aead(t: CipherType, key: &[u8], payload: &[u8]) -> io::Result<Vec<u8>> {
let tag_size = t.tag_size();
let iv_size = t.iv_size();
if payload.len() < tag_size + iv_size {
let err = io::Error::new(io::ErrorKind::Other, "udp packet too short");
return Err(err);
}
let nounce = &payload[..iv_size];
let data = &payload[iv_size..payload.len() - tag_size];
let tag = &payload[payload.len() - tag_size..];
let data_length = payload.len() - tag_size - iv_size;
let mut cipher = crypto::new_aead_decryptor(t, key, nounce);
let mut recv_payload = vec![0u8; data_length];
try!(cipher.decrypt(data, &mut recv_payload, tag));
Ok(recv_payload)
}

View File

@@ -77,11 +77,9 @@ use relay::{BoxIoFuture, boxed_future};
use relay::loadbalancing::server::{LoadBalancer, RoundRobin};
use relay::dns_resolver::DnsResolver;
use relay::socks5::{Address, UdpAssociateHeader};
use crypto::{self, StreamCipher};
use crypto::CryptoMode;
use super::{MAXIMUM_ASSOCIATE_MAP_SIZE, MAXIMUM_UDP_PAYLOAD_SIZE};
use super::{send_to, recv_from};
use super::crypto_io::{encrypt_payload, decrypt_payload};
type AssociateMap = LruCache<Address, SocketAddr>;
type ServerCache = LruCache<SocketAddr, Rc<ServerConfig>>;
@@ -126,24 +124,7 @@ impl Client {
svr_cfg.addr(),
buf.len());
let iv_len = svr_cfg.method().iv_size();
if buf.len() < iv_len {
error!("Invalid ShadowSocks UDP packet, expected IV length {}, packet length {}",
iv_len,
buf.len());
let err = io::Error::new(io::ErrorKind::Other, "early eof");
return Err(err);
}
let iv = &buf[..iv_len];
let mut cipher = crypto::new_stream(svr_cfg.method(), svr_cfg.key(), iv, CryptoMode::Decrypt);
// Decrypt payload with cipher
let mut payload = Vec::with_capacity(buf.len());
try!(cipher.update(&buf[iv_len..], &mut payload));
try!(cipher.finalize(&mut payload));
Ok(payload)
decrypt_payload(svr_cfg.method(), svr_cfg.key(), buf)
})
.and_then(move |payload| {
// Get Address from the front of payload (ShadowSocks protocol)
@@ -180,7 +161,7 @@ impl Client {
})
})
.and_then(|(client_addr, assoc, reply_body)| {
send_to(socket, reply_body, client_addr).map(move |(socket, _, _)| {
socket.send_dgram(reply_body, client_addr).map(move |(socket, _)| {
Client {
assoc: assoc,
servers: servers,
@@ -235,26 +216,15 @@ impl Client {
let svr_cfg = server_picker.borrow_mut().pick_server();
// Client -> Proxy
let iv = svr_cfg.method().gen_init_vec();
let mut cipher = crypto::new_stream(svr_cfg.method(),
svr_cfg.key(),
&iv[..],
CryptoMode::Encrypt);
let payload_buf = Vec::with_capacity(payload.len());
// Append Address to the front (ShadowSocks protocol)
assoc_addr.write_to(payload_buf)
assoc_addr.write_to(Vec::with_capacity(payload.len()))
.and_then(move |mut payload_buf| {
payload_buf.extend_from_slice(&payload[header_len..]);
Ok(payload_buf)
})
.and_then(move |payload| -> io::Result<_> {
// Encrypt the whole body as payload
let mut send_payload = Vec::with_capacity(iv.len() + payload.len());
send_payload.extend_from_slice(&iv[..]);
try!(cipher.update(&payload[..], &mut send_payload));
try!(cipher.finalize(&mut send_payload));
Ok((svr_cfg, send_payload))
encrypt_payload(svr_cfg.method(), svr_cfg.key(), &payload).map(move |b| (svr_cfg, b))
})
.map_err(From::from)
.and_then(move |(svr_cfg, payload)| {
@@ -267,8 +237,8 @@ impl Client {
svrs_ref.insert(addr, svr_cfg.clone());
}
send_to(socket, payload, addr).map(|(socket, body, len)| {
trace!("Body size: {}, sent packet size: {}", body.len(), len);
socket.send_dgram(payload, addr).map(|(socket, body)| {
trace!("Sent body, size: {}", body.len());
Client {
assoc: assoc,
server_picker: server_picker,
@@ -287,7 +257,7 @@ impl Client {
fn handle_once(self) -> BoxIoFuture<Client> {
let Client { assoc, server_picker, servers, socket } = self;
let fut = recv_from(socket, vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE]).and_then(move |(socket, buf, n, src)| {
let fut = socket.recv_dgram(vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE]).and_then(move |(socket, buf, n, src)| {
// Reassemble Client
let c = Client {
assoc: assoc,

View File

@@ -21,19 +21,15 @@
//! Relay for UDP implementation
use std::net::SocketAddr;
use std::mem;
use std::io;
use tokio_core::net::UdpSocket;
use net2::UdpBuilder;
use futures::{Future, Poll, Async};
pub mod local;
pub mod server;
mod crypto_io;
/// The maximum UDP payload size (defined in the original shadowsocks Python)
///
/// *I cannot find any references about why clowwindy used this value as the maximum
@@ -44,85 +40,6 @@ pub const MAXIMUM_UDP_PAYLOAD_SIZE: usize = 65536;
/// Maximum associations to maintain
pub const MAXIMUM_ASSOCIATE_MAP_SIZE: usize = 65536;
/// Future for `recv_from`
pub enum RecvFromUdpSocket<B: AsMut<[u8]>> {
Pending { socket: UdpSocket, buf: B },
Empty,
}
impl<B: AsMut<[u8]>> Future for RecvFromUdpSocket<B> {
type Item = (UdpSocket, B, usize, SocketAddr);
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let (length, src) = match *self {
RecvFromUdpSocket::Empty => panic!("poll after RecvFromUdpSocket is finished"),
RecvFromUdpSocket::Pending { ref socket, ref mut buf } => {
if socket.poll_read().is_not_ready() {
return Ok(Async::NotReady);
}
try_nb!(socket.recv_from(buf.as_mut()))
}
};
match mem::replace(self, RecvFromUdpSocket::Empty) {
RecvFromUdpSocket::Pending { socket, buf } => Ok((socket, buf, length, src).into()),
RecvFromUdpSocket::Empty => unreachable!(),
}
}
}
/// Receive from UdpSocket
pub fn recv_from<B: AsMut<[u8]>>(socket: UdpSocket, buf: B) -> RecvFromUdpSocket<B> {
RecvFromUdpSocket::Pending {
socket: socket,
buf: buf,
}
}
/// Future for `send_to`
pub enum SendToUdpSocket<B: AsRef<[u8]>> {
Pending {
socket: UdpSocket,
buf: B,
target_addr: SocketAddr,
},
Empty,
}
impl<B: AsRef<[u8]>> Future for SendToUdpSocket<B> {
type Item = (UdpSocket, B, usize);
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let length = match self {
&mut SendToUdpSocket::Empty => panic!("poll after SendToUdpSocket is finished"),
&mut SendToUdpSocket::Pending { ref socket, ref buf, ref target_addr } => {
if socket.poll_write().is_not_ready() {
return Ok(Async::NotReady);
}
try_nb!(socket.send_to(buf.as_ref(), target_addr))
}
};
match mem::replace(self, SendToUdpSocket::Empty) {
SendToUdpSocket::Pending { socket, buf, .. } => Ok((socket, buf, length).into()),
SendToUdpSocket::Empty => unreachable!(),
}
}
}
/// Send data to `UdpSocket`
pub fn send_to<B: AsRef<[u8]>>(socket: UdpSocket, buf: B, target: SocketAddr) -> SendToUdpSocket<B> {
SendToUdpSocket::Pending {
socket: socket,
buf: buf,
target_addr: target,
}
}
#[cfg(unix)]
fn reuse_port(builder: &UdpBuilder) -> io::Result<&UdpBuilder> {
use net2::unix::UnixUdpBuilderExt;

View File

@@ -41,11 +41,9 @@ use config::{Config, ServerConfig};
use relay::{BoxIoFuture, boxed_future};
use relay::dns_resolver::DnsResolver;
use relay::socks5::Address;
use crypto::{self, StreamCipher};
use crypto::CryptoMode;
use super::{MAXIMUM_ASSOCIATE_MAP_SIZE, MAXIMUM_UDP_PAYLOAD_SIZE};
use super::{send_to, recv_from};
use super::crypto_io::{encrypt_payload, decrypt_payload};
#[derive(Debug, Clone)]
struct Associate {
@@ -72,25 +70,7 @@ impl ConnectionContext {
// Client -> Remote
let fut = futures::lazy(move || {
let buf = &buf[..n];
let iv_len = svr_cfg.method().iv_size();
if buf.len() < iv_len {
error!("Invalid ShadowSocks UDP packet, expected IV length {}, packet length {}",
iv_len,
buf.len());
let err = io::Error::new(io::ErrorKind::Other, "early eof");
return Err(err);
}
let iv = &buf[..iv_len];
let mut cipher = crypto::new_stream(svr_cfg.method(), svr_cfg.key(), iv, CryptoMode::Decrypt);
// Decrypt payload from Client
let mut payload = Vec::with_capacity(buf.len());
try!(cipher.update(&buf[iv_len..], &mut payload));
try!(cipher.finalize(&mut payload));
Ok((payload, svr_cfg))
decrypt_payload(svr_cfg.method(), svr_cfg.key(), buf).map(move |b| (b, svr_cfg))
})
.and_then(move |(payload, svr_cfg)| {
// Read Address in the front (ShadowSocks protocol)
@@ -117,10 +97,10 @@ impl ConnectionContext {
client_addr: src,
});
send_to(socket, body, remote_addr)
socket.send_dgram(body, remote_addr)
})
.map(move |(socket, body, len)| {
trace!("Sent body {} bytes, actual {} bytes", body.len(), len);
.map(move |(socket, body)| {
trace!("Sent body, len: {} bytes", body.len());
ConnectionContext {
assoc: assoc,
svr_cfg: svr_cfg,
@@ -149,26 +129,20 @@ impl ConnectionContext {
buf_len);
// Client <- Remote
let mut iv = svr_cfg.method().gen_init_vec();
let mut cipher = crypto::new_stream(svr_cfg.method(),
svr_cfg.key(),
&iv[..],
CryptoMode::Encrypt);
// Append Address in front of body (ShadowSocks protocol)
let cloned_svr_cfg = svr_cfg.clone();
let fut = address.write_to(Vec::with_capacity(buf_len))
.map(move |mut send_buf| {
send_buf.extend_from_slice(&buf[..n]);
send_buf
})
.and_then(move |send_buf| -> io::Result<_> {
try!(cipher.update(&send_buf[..], &mut iv));
try!(cipher.finalize(&mut iv));
Ok(iv)
let svr_cfg = cloned_svr_cfg;
encrypt_payload(svr_cfg.method(), svr_cfg.key(), &send_buf)
})
.and_then(move |final_buf| send_to(socket, final_buf, client_addr))
.map(|(socket, buf, len)| {
trace!("Sent body {} actual {}", buf.len(), len);
.and_then(move |final_buf| socket.send_dgram(final_buf, client_addr))
.map(|(socket, buf)| {
trace!("Sent body len: {}", buf.len());
ConnectionContext {
assoc: assoc,
svr_cfg: svr_cfg,
@@ -183,7 +157,7 @@ impl ConnectionContext {
fn handle_once(self) -> BoxIoFuture<ConnectionContext> {
let ConnectionContext { assoc, svr_cfg, socket } = self;
let fut = recv_from(socket, vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE])
let fut = socket.recv_dgram(vec![0u8; MAXIMUM_UDP_PAYLOAD_SIZE])
.and_then(move |(socket, buf, n, src)| {
let c = ConnectionContext {
assoc: assoc.clone(),