generated from tipragot/rust
Adding Aes128Gcm encryption #18
194
Cargo.lock
generated
194
Cargo.lock
generated
|
@ -2,6 +2,200 @@
|
||||||
# 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]]
|
||||||
|
name = "bevnet"
|
||||||
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "border-wars"
|
name = "border-wars"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
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]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"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]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.153"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
|
[[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]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[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]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
|
@ -10,3 +10,6 @@ categories = ["network-programming", "game-development"]
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aes-gcm = "0.10.3"
|
||||||
|
|
|
@ -4,6 +4,9 @@ use std::collections::LinkedList;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
|
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
|
||||||
|
|
||||||
|
use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng};
|
||||||
|
use aes_gcm::{Aes128Gcm, Key, Nonce};
|
||||||
|
|
||||||
/// A non-blocking tcp connection.
|
/// A non-blocking tcp connection.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -14,8 +17,9 @@ use std::net::{TcpListener, TcpStream, ToSocketAddrs};
|
||||||
/// 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;
|
||||||
|
@ -56,6 +60,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
|
||||||
|
@ -64,26 +71,36 @@ 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 {
|
||||||
|
/// Generates a new secret key.
|
||||||
|
pub fn generate_key() -> [u8; 16] {
|
||||||
|
Aes128Gcm::generate_key(OsRng).into()
|
||||||
|
}
|
||||||
|
|
||||||
/// 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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [Connection] that connects to the given address.
|
/// Creates a new [Connection] that connects to the given address.
|
||||||
///
|
///
|
||||||
/// This function is blocking.
|
/// This function is blocking.
|
||||||
pub fn connect(address: impl ToSocketAddrs) -> io::Result<Self> {
|
pub fn connect(address: impl ToSocketAddrs, secret_key: &[u8; 16]) -> io::Result<Self> {
|
||||||
Self::new(TcpStream::connect(address)?)
|
Self::new(TcpStream::connect(address)?, secret_key.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a message over the connection.
|
/// Sends a message over the connection.
|
||||||
|
@ -93,6 +110,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,
|
||||||
|
@ -105,10 +131,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()
|
||||||
|
@ -190,7 +216,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,
|
||||||
|
@ -212,13 +238,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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +288,9 @@ impl Connection {
|
||||||
/// 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;
|
||||||
|
@ -244,14 +303,17 @@ impl Connection {
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Listener(TcpListener);
|
pub struct Listener(TcpListener, Key<Aes128Gcm>);
|
||||||
|
|
||||||
impl Listener {
|
impl Listener {
|
||||||
/// Creates a new listener.
|
/// Creates a new listener.
|
||||||
pub fn bind(addr: impl ToSocketAddrs) -> io::Result<Listener> {
|
pub fn bind(addr: impl ToSocketAddrs, secret_key: &[u8; 16]) -> io::Result<Self> {
|
||||||
let listener = TcpListener::bind(addr)?;
|
let listener = TcpListener::bind(addr)?;
|
||||||
listener.set_nonblocking(true)?;
|
listener.set_nonblocking(true)?;
|
||||||
Ok(Listener(listener))
|
Ok(Self(
|
||||||
|
listener,
|
||||||
|
Key::<Aes128Gcm>::from_slice(secret_key).to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accepts a new [Connection].
|
/// Accepts a new [Connection].
|
||||||
|
@ -259,7 +321,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.1).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),
|
||||||
|
|
Loading…
Reference in a new issue