Merge branch 'main' into upnp
Some checks failed
Rust Checks / checks (push) Failing after 11s

This commit is contained in:
Tipragot 2024-02-07 17:18:05 +01:00
commit f984920dbd
3 changed files with 228 additions and 14 deletions

150
Cargo.lock generated
View file

@ -2,6 +2,41 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "aes"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "aes-gcm"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]] [[package]]
name = "attohttpc" name = "attohttpc"
version = "0.16.3" version = "0.16.3"
@ -24,6 +59,7 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
name = "bevnet" name = "bevnet"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"aes-gcm",
"base64", "base64",
"igd", "igd",
"local-ip-address", "local-ip-address",
@ -51,6 +87,45 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"rand_core",
"typenum",
]
[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.9.0" version = "1.9.0"
@ -72,6 +147,16 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.12" version = "0.2.12"
@ -83,6 +168,16 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "ghash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.11" version = "0.2.11"
@ -117,6 +212,15 @@ dependencies = [
"xmltree", "xmltree",
] ]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.10" version = "1.0.10"
@ -172,12 +276,30 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "polyval"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.17" version = "0.2.17"
@ -252,6 +374,12 @@ dependencies = [
"syn 2.0.48", "syn 2.0.48",
] ]
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -309,6 +437,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.15" version = "0.3.15"
@ -330,6 +464,16 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.0" version = "2.5.0"
@ -341,6 +485,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View file

@ -13,5 +13,6 @@ workspace = true
[dependencies] [dependencies]
local-ip-address = "0.5.7" local-ip-address = "0.5.7"
aes-gcm = "0.10.3"
base64 = "0.21.7" base64 = "0.21.7"
igd = "0.12.1" igd = "0.12.1"

View file

@ -4,6 +4,8 @@ use std::collections::LinkedList;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, TcpListener, TcpStream}; use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, TcpListener, TcpStream};
use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng};
use aes_gcm::{Aes128Gcm, Key, Nonce};
use base64::prelude::*; use base64::prelude::*;
use igd::{Gateway, PortMappingProtocol}; use igd::{Gateway, PortMappingProtocol};
use local_ip_address::local_ip; use local_ip_address::local_ip;
@ -18,8 +20,9 @@ use local_ip_address::local_ip;
/// use bevnet::{Connection, Listener}; /// use bevnet::{Connection, Listener};
/// ///
/// # fn main() -> io::Result<()> { /// # fn main() -> io::Result<()> {
/// let listener = Listener::bind("127.0.0.1:23732")?; /// let secret_key = Connection::generate_key();
/// let mut connection = Connection::connect("127.0.0.1:23732")?; /// let listener = Listener::bind("127.0.0.1:23732", &secret_key)?;
/// let mut connection = Connection::connect("127.0.0.1:23732", &secret_key)?;
/// ///
/// // The accept operation is not blocking. So we need to loop here. /// // The accept operation is not blocking. So we need to loop here.
/// let mut server_connection; /// let mut server_connection;
@ -60,6 +63,9 @@ pub struct Connection {
/// `None` if the message length is not yet received. /// `None` if the message length is not yet received.
receive_message_len: Option<u16>, receive_message_len: Option<u16>,
/// The nonce used for encryption.
receive_message_nonce: Option<Vec<u8>>,
/// The length of the received byte block. /// The length of the received byte block.
/// ///
/// Used by [Connection::receive_partial] to determine if the block is /// Used by [Connection::receive_partial] to determine if the block is
@ -68,18 +74,23 @@ pub struct Connection {
/// The buffer used to receive a byte block. /// The buffer used to receive a byte block.
receive_buffer: Vec<u8>, receive_buffer: Vec<u8>,
/// The secret key used for encryption.
secret_key: Aes128Gcm,
} }
impl Connection { impl Connection {
/// Creates a new [Connection] from a [TcpStream]. /// Creates a new [Connection] from a [TcpStream].
fn new(stream: TcpStream) -> io::Result<Self> { fn new(stream: TcpStream, secret_key: &Key<Aes128Gcm>) -> io::Result<Self> {
stream.set_nonblocking(true)?; stream.set_nonblocking(true)?;
Ok(Self { Ok(Self {
stream, stream,
send_buffers: LinkedList::new(), send_buffers: LinkedList::new(),
receive_message_len: None, receive_message_len: None,
receive_message_nonce: None,
receive_filled_len: 0, receive_filled_len: 0,
receive_buffer: Vec::new(), receive_buffer: Vec::new(),
secret_key: Aes128Gcm::new(secret_key),
}) })
} }
@ -105,7 +116,10 @@ impl Connection {
) )
})?), })?),
); );
Self::new(TcpStream::connect(address)?) Self::new(
TcpStream::connect(address)?,
Key::<Aes128Gcm>::from_slice(&data[6..]),
)
} }
/// Sends a message over the connection. /// Sends a message over the connection.
@ -115,6 +129,15 @@ impl Connection {
/// ///
/// This function is not blocking. /// This function is not blocking.
pub fn send(&mut self, message: &[u8]) -> io::Result<bool> { pub fn send(&mut self, message: &[u8]) -> io::Result<bool> {
// Encrypt the message.
let nonce = Aes128Gcm::generate_nonce(OsRng);
let message = self.secret_key.encrypt(&nonce, message).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("failed to encrypt message: {}", e),
)
})?;
// Get the length of the message as a u16. // Get the length of the message as a u16.
let message_len: u16 = match message.len().try_into() { let message_len: u16 = match message.len().try_into() {
Ok(len) => len, Ok(len) => len,
@ -127,10 +150,10 @@ impl Connection {
}; };
// Add a new buffer to the send queue. // Add a new buffer to the send queue.
let mut buffer = Vec::with_capacity(message_len as usize + 2); self.send_buffers
buffer.extend(message_len.to_ne_bytes()); .push_back((0, message_len.to_ne_bytes().to_vec()));
buffer.extend(message); self.send_buffers.push_back((0, nonce.to_vec()));
self.send_buffers.push_back((0, buffer)); self.send_buffers.push_back((0, message));
// Update the connection. // Update the connection.
self.update() self.update()
@ -193,6 +216,12 @@ impl Connection {
let receive_buffer = &mut self.receive_buffer[start_index..start_index + len]; let receive_buffer = &mut self.receive_buffer[start_index..start_index + len];
let received_len = self.stream.read(receive_buffer); let received_len = self.stream.read(receive_buffer);
self.receive_filled_len += match received_len { self.receive_filled_len += match received_len {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::ConnectionAborted,
"connection closed by remote peer",
));
}
Ok(n) => n, Ok(n) => n,
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(false), Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(false),
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => return Ok(false), Err(ref e) if e.kind() == io::ErrorKind::Interrupted => return Ok(false),
@ -212,7 +241,7 @@ impl Connection {
/// If no message is available, returns `None`. /// If no message is available, returns `None`.
/// ///
/// This function is not blocking. /// This function is not blocking.
pub fn receive(&mut self) -> io::Result<Option<&[u8]>> { pub fn receive(&mut self) -> io::Result<Option<Vec<u8>>> {
// Receiving the message length. // Receiving the message length.
let message_len = match self.receive_message_len { let message_len = match self.receive_message_len {
Some(message_len) => message_len, Some(message_len) => message_len,
@ -234,13 +263,45 @@ impl Connection {
} }
}; };
if self.receive_message_nonce.is_none() {
// If the nonce is not received yet, return `None`.
if !self.receive_partial(12)? {
return Ok(None);
}
// Setting the nonce.
self.receive_message_nonce = Some(self.receive_buffer[..12].to_vec());
}
// Receiving the message. // Receiving the message.
if !self.receive_partial(message_len)? { if !self.receive_partial(message_len)? {
return Ok(None); return Ok(None);
} }
let message = &self.receive_buffer[..message_len as usize];
// Getting the nonce.
let nonce = self
.receive_message_nonce
.as_ref()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "missing nonce"))?;
// Decrypting the message.
let message = self
.secret_key
.decrypt(Nonce::from_slice(nonce), message)
.map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("failed to decrypt message: {}", e),
)
})?;
// Resetting the message length and nonce.
self.receive_message_len = None;
self.receive_message_nonce = None;
// Returning the message. // Returning the message.
Ok(Some(&self.receive_buffer[..message_len as usize])) Ok(Some(message))
} }
} }
@ -252,8 +313,9 @@ impl Connection {
/// use bevnet::{Connection, Listener}; /// use bevnet::{Connection, Listener};
/// ///
/// # fn main() -> io::Result<()> { /// # fn main() -> io::Result<()> {
/// let listener = Listener::new()?; /// let secret_key = Connection::generate_key();
/// let mut connection = Connection::connect("127.0.0.1:23732")?; /// let listener = Listener::new(&secret_key)?;
/// let mut connection = Connection::connect(listener.connection_string())?;
/// ///
/// // The accept operation is not blocking. So we need to loop here. /// // The accept operation is not blocking. So we need to loop here.
/// let mut server_connection; /// let mut server_connection;
@ -266,7 +328,7 @@ impl Connection {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub struct Listener(TcpListener, Gateway, SocketAddrV4); pub struct Listener(TcpListener, Gateway, SocketAddrV4, Key<Aes128Gcm>);
impl Listener { impl Listener {
/// Creates a new listener. /// Creates a new listener.
@ -291,6 +353,7 @@ impl Listener {
listener, listener,
gateway, gateway,
SocketAddrV4::new(external_address, opened_port), SocketAddrV4::new(external_address, opened_port),
Aes128Gcm::generate_key(OsRng),
)) ))
} }
@ -299,7 +362,7 @@ impl Listener {
/// This function is not blocking. /// This function is not blocking.
pub fn accept(&self) -> io::Result<Option<Connection>> { pub fn accept(&self) -> io::Result<Option<Connection>> {
match self.0.accept() { match self.0.accept() {
Ok((stream, _)) => Connection::new(stream).map(Some), Ok((stream, _)) => Connection::new(stream, &self.3).map(Some),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None), Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => Ok(None), Err(ref e) if e.kind() == io::ErrorKind::Interrupted => Ok(None),
Err(e) => Err(e), Err(e) => Err(e),