✨♻️➖➕ bevy_vanth, vanth, vanth_derive, vanth_transport: Many changes in preparation for networking
- Created new crate `bevy_vanth` with basic plugin structure for Bevy integration. - Refactored `Id` generation in `vanth` to use `OsRng` and removed redundant `to_u128_pair`/`from_u128_pair` methods. - Moved networking functionality into new `net` module with `Node`, `Packet`, and `Message` types. - Updated `vanth_derive` to use `proc-macro-crate` for reliable crate path resolution. - Added `rand` dependency to replace custom ID generation logic. - Updated `Cargo.toml`/`Cargo.lock` with new dependencies: `bevy_app`, `nix`, `cfg_aliases`, `proc-macro-crate`. - Modified `README.md` with improved project description. - Added commented clippy check in `flake.nix`. - Added `smol`, `async-process`, and `async-trait` dependencies in root and `vanth` crate. - Integrated `vanth` crate into `bevy_vanth` and added serde dependency. - Reorganized test files into module structure for `cli` and `vanth` crates. - Created new modules `compress` and `ecc` in `vanth`. - Implemented `Node` with async `run` method and `Backend` trait for networking in `vanth`. - Renamed `Memory` backend to `InMemoryStore` in `vanth` store module.
This commit is contained in:
parent
5262a266c0
commit
5afe3b61fb
34 changed files with 1887 additions and 483 deletions
25
crates/vanth_transport/Cargo.toml
Normal file
25
crates/vanth_transport/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "vanth_transport"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
digest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
blake3.workspace = true
|
||||
vanth = { path = "../vanth" }
|
||||
vanth_derive = { path = "../vanth_derive" }
|
||||
tracing.workspace = true
|
||||
rand = { workspace = true }
|
||||
smol.workspace = true
|
||||
async-process.workspace = true
|
||||
async-trait.workspace = true
|
||||
ntrulp = { path = "/home/asraelite/dev/ntrulp", features = ["ntrup1277"] }
|
||||
chacha20poly1305.workspace = true
|
||||
serde_bytes.workspace = true
|
||||
serde_arrays.workspace = true
|
26
crates/vanth_transport/src/crypto.rs
Normal file
26
crates/vanth_transport/src/crypto.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use vanth::Vanth;
|
||||
|
||||
pub mod ntru;
|
||||
|
||||
pub enum CryptoError {
|
||||
RngGenerationFailed,
|
||||
InvalidKey,
|
||||
}
|
||||
|
||||
pub trait Asymmetric {
|
||||
type PublicKey: Vanth + Serialize + DeserializeOwned;
|
||||
type PrivateKey: Vanth + Serialize + DeserializeOwned;
|
||||
type Seed;
|
||||
|
||||
fn generate_default_public_key(&mut self, private_key: &Self::PrivateKey) -> Result<Self::PublicKey, CryptoError>;
|
||||
fn generate_private_key(&mut self, seed: Self::Seed) -> Result<Self::PrivateKey, CryptoError>;
|
||||
}
|
||||
|
||||
pub trait MultiKeyAsymmetric: Asymmetric {
|
||||
fn generate_public_key(
|
||||
&mut self,
|
||||
seed: Self::Seed,
|
||||
private_key: &Self::PrivateKey,
|
||||
) -> Result<Self::PublicKey, CryptoError>;
|
||||
}
|
151
crates/vanth_transport/src/crypto/ntru.rs
Normal file
151
crates/vanth_transport/src/crypto/ntru.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use super::{Asymmetric, CryptoError};
|
||||
use ntrulp::{
|
||||
key::{priv_key::PrivKey, pub_key::PubKey},
|
||||
poly::{r3::R3, rq::Rq},
|
||||
};
|
||||
use serde::{Deserialize, Serialize, ser::SerializeStruct as _};
|
||||
use vanth::Vanth;
|
||||
|
||||
const NTRU_COEFFS_LEN: usize = 1277;
|
||||
|
||||
pub struct Ntru {}
|
||||
|
||||
#[derive(Vanth)]
|
||||
pub struct PrivateKey(PrivKey);
|
||||
|
||||
impl From<PrivKey> for PrivateKey {
|
||||
fn from(value: PrivKey) -> Self {
|
||||
PrivateKey(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PrivateKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("PrivateKey", 2)?;
|
||||
s.serialize_field("f", &self.0.0.coeffs.to_vec())?;
|
||||
s.serialize_field("g", &self.0.1.coeffs.to_vec())?;
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PrivateKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct PrivateKeyHelper {
|
||||
f: Vec<i8>,
|
||||
g: Vec<i8>,
|
||||
}
|
||||
|
||||
let helper = PrivateKeyHelper::deserialize(deserializer)?;
|
||||
|
||||
let f = {
|
||||
let slice = helper.f.as_slice();
|
||||
if slice.len() != NTRU_COEFFS_LEN {
|
||||
return Err(serde::de::Error::invalid_length(
|
||||
slice.len(),
|
||||
&"invalid coeffs length for PrivateKey.f",
|
||||
));
|
||||
}
|
||||
let mut coeffs = [0i8; NTRU_COEFFS_LEN];
|
||||
coeffs.copy_from_slice(slice);
|
||||
R3 { coeffs }
|
||||
};
|
||||
let g = {
|
||||
let slice = helper.g.as_slice();
|
||||
if slice.len() != NTRU_COEFFS_LEN {
|
||||
return Err(serde::de::Error::invalid_length(
|
||||
slice.len(),
|
||||
&"invalid coeffs length for PrivateKey.g",
|
||||
));
|
||||
}
|
||||
let mut coeffs = [0i8; NTRU_COEFFS_LEN];
|
||||
coeffs.copy_from_slice(slice);
|
||||
R3 { coeffs }
|
||||
};
|
||||
|
||||
Ok(PrivateKey(PrivKey(f, g)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Vanth)]
|
||||
pub struct PublicKey(PubKey);
|
||||
|
||||
impl From<PubKey> for PublicKey {
|
||||
fn from(value: PubKey) -> Self {
|
||||
PublicKey(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PublicKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("PublicKey", 1)?;
|
||||
s.serialize_field("coeffs", &self.0.coeffs.to_vec())?;
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PublicKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct PublicKeyHelper {
|
||||
coeffs: Vec<i16>,
|
||||
}
|
||||
|
||||
let helper = PublicKeyHelper::deserialize(deserializer)?;
|
||||
let slice = helper.coeffs.as_slice();
|
||||
if slice.len() != NTRU_COEFFS_LEN {
|
||||
return Err(serde::de::Error::invalid_length(
|
||||
slice.len(),
|
||||
&"invalid coeffs length for PublicKey.coeffs",
|
||||
));
|
||||
}
|
||||
let mut coeffs = [0i16; NTRU_COEFFS_LEN];
|
||||
coeffs.copy_from_slice(slice);
|
||||
Ok(PublicKey(Rq { coeffs }))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use seed during generation, or in some other way make the random number generator more generic.
|
||||
impl Asymmetric for Ntru {
|
||||
type PrivateKey = PrivateKey;
|
||||
type PublicKey = PublicKey;
|
||||
// TODO: Use correct type
|
||||
type Seed = u64;
|
||||
|
||||
fn generate_default_public_key(&mut self, private_key: &Self::PrivateKey) -> Result<Self::PublicKey, CryptoError> {
|
||||
let pubkey = PubKey::from_sk(&private_key.0).map_err(|_| CryptoError::InvalidKey)?;
|
||||
Ok(PublicKey(pubkey))
|
||||
}
|
||||
|
||||
fn generate_private_key(&mut self, seed: Self::Seed) -> Result<Self::PrivateKey, CryptoError> {
|
||||
let mut rng = rand::rng();
|
||||
let f: Rq = Rq::from(ntrulp::rng::short_random(&mut rng).unwrap());
|
||||
let mut g: R3;
|
||||
let mut attempt_count = 0;
|
||||
let sk: PrivKey = loop {
|
||||
g = R3::from(ntrulp::rng::random_small(&mut rng));
|
||||
|
||||
if let Ok(s) = PrivKey::compute(&f, &g) {
|
||||
break s;
|
||||
};
|
||||
|
||||
attempt_count += 1;
|
||||
if attempt_count > 100 {
|
||||
return Err(CryptoError::RngGenerationFailed);
|
||||
}
|
||||
};
|
||||
Ok(PrivateKey(sk))
|
||||
}
|
||||
}
|
44
crates/vanth_transport/src/lib.rs
Normal file
44
crates/vanth_transport/src/lib.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use smol::net::UdpSocket;
|
||||
use vanth::{
|
||||
entity::Id,
|
||||
network::{Address, Backend},
|
||||
};
|
||||
|
||||
pub mod crypto;
|
||||
|
||||
pub struct UdpBackend {
|
||||
open_sockets: HashMap<Id<UdpSocket>, UdpSocket>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Backend for UdpBackend {
|
||||
type ConnectionHandle = Id<UdpSocket>;
|
||||
type Error = String;
|
||||
|
||||
async fn connect<'a>(&mut self, address: &Address) -> Result<Self::ConnectionHandle, Self::Error> {
|
||||
let socket = match address {
|
||||
Address::Uri(uri) => UdpSocket::bind(uri).await.map_err(|e| e.to_string())?,
|
||||
_ => return Err(format!("Address {:?} is not supported for the UDP backend.", address)),
|
||||
};
|
||||
let id = Id::random();
|
||||
assert!(self.open_sockets.insert(id, socket).is_none());
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
async fn close(&mut self, connection: Self::ConnectionHandle) -> Result<(), Self::Error> {
|
||||
todo!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn poll(&mut self) -> impl Iterator<Item = Result<(Self::ConnectionHandle, Vec<u8>), Self::Error>> {
|
||||
todo!();
|
||||
[].into_iter()
|
||||
}
|
||||
|
||||
async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
50
crates/vanth_transport/tests/ntru_serde_roundtrip.rs
Normal file
50
crates/vanth_transport/tests/ntru_serde_roundtrip.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use serde_json::{from_value, to_value, Value};
|
||||
use vanth_transport::crypto::ntru::{PrivateKey, PublicKey};
|
||||
|
||||
use ntrulp::{
|
||||
key::{priv_key::PrivKey as NtruPrivKey, pub_key::PubKey as NtruPubKey},
|
||||
poly::{r3::R3, rq::Rq},
|
||||
};
|
||||
|
||||
fn generate_keypair() -> (NtruPrivKey, NtruPubKey) {
|
||||
let mut rng = rand::rng();
|
||||
let f: Rq = Rq::from(ntrulp::rng::short_random(&mut rng).expect("failed to sample short_random"));
|
||||
let mut g: R3;
|
||||
|
||||
let sk = loop {
|
||||
g = R3::from(ntrulp::rng::random_small(&mut rng));
|
||||
match NtruPrivKey::compute(&f, &g) {
|
||||
Ok(s) => break s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
};
|
||||
|
||||
let pk = NtruPubKey::from_sk(&sk).expect("failed to derive public key from secret key");
|
||||
(sk, pk)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_private_key_serde_roundtrip() {
|
||||
let (sk, _) = generate_keypair();
|
||||
|
||||
// Round-trip via serde_json Value without touching internal fields.
|
||||
let wrapped: PrivateKey = sk.into();
|
||||
let json1: Value = to_value(&wrapped).expect("failed to serialize PrivateKey");
|
||||
let reparsed: PrivateKey = from_value(json1.clone()).expect("failed to deserialize PrivateKey");
|
||||
let json2: Value = to_value(&reparsed).expect("failed to serialize PrivateKey again");
|
||||
|
||||
assert_eq!(json2, json1, "PrivateKey JSON did not round-trip losslessly.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_public_key_serde_roundtrip() {
|
||||
let (_, pk) = generate_keypair();
|
||||
|
||||
// Round-trip via serde_json Value without touching internal fields.
|
||||
let wrapped: PublicKey = pk.into();
|
||||
let json1: Value = to_value(&wrapped).expect("failed to serialize PublicKey");
|
||||
let reparsed: PublicKey = from_value(json1.clone()).expect("failed to deserialize PublicKey");
|
||||
let json2: Value = to_value(&reparsed).expect("failed to serialize PublicKey again");
|
||||
|
||||
assert_eq!(json2, json1, "PublicKey JSON did not round-trip losslessly.");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue