♻️ 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:
Markus Scully 2025-08-17 11:56:34 +03:00
parent 5262a266c0
commit 5afe3b61fb
Signed by: mascully
GPG key ID: 93CA5814B698101C
34 changed files with 1887 additions and 483 deletions

View 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

View 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>;
}

View 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))
}
}

View 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!()
}
}

View 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.");
}