mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-08 22:38:01 -06:00
Add more state objects.
This commit is contained in:
Generated
+47
@@ -269,6 +269,26 @@ version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
|
||||
dependencies = [
|
||||
"const-random-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random-macro"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
"once_cell",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
@@ -350,6 +370,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
@@ -578,6 +604,17 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
@@ -1757,6 +1794,15 @@ version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.11.0"
|
||||
@@ -1824,6 +1870,7 @@ name = "unshell"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"const-random",
|
||||
"crossbeam-channel",
|
||||
"ratatui",
|
||||
"rkyv",
|
||||
|
||||
+16
-5
@@ -18,12 +18,14 @@ repository = "https://github.com/Astatin3/unshell"
|
||||
include = ["LICENSE", "**/*.rs", "Cargo.toml"]
|
||||
|
||||
[workspace.dependencies]
|
||||
rkyv = "0.8.16"
|
||||
thiserror = "2.0.18"
|
||||
chrono = "0.4.44"
|
||||
static_init = "1.0.4"
|
||||
rkyv = "0.8.16"
|
||||
thiserror = "2.0.18"
|
||||
chrono = "0.4.44"
|
||||
static_init = "1.0.4"
|
||||
portable-pty = "0.9.0"
|
||||
crossbeam-channel = "0.5.15"
|
||||
const-random = "0.1.18"
|
||||
|
||||
|
||||
ratatui = "0.30.0"
|
||||
|
||||
@@ -44,7 +46,7 @@ edition.workspace = true
|
||||
description = "Pure no_std implementation of the UnShell Protocol"
|
||||
|
||||
[features]
|
||||
# default = ["interface_ratatui"]
|
||||
default = ["counter_shuffle_feistel_lcg"]
|
||||
|
||||
log = []
|
||||
log_debug = ["log", "dep:chrono"]
|
||||
@@ -52,17 +54,26 @@ log_debug = ["log", "dep:chrono"]
|
||||
interface = []
|
||||
interface_ratatui = ["interface", "dep:ratatui"]
|
||||
|
||||
counter_shuffle_none = []
|
||||
counter_shuffle_feistel = []
|
||||
counter_shuffle_feistel_lcg = []
|
||||
|
||||
[dependencies]
|
||||
rkyv = { workspace = true }
|
||||
thiserror = { workspace = true, optional = true }
|
||||
chrono = { workspace = true, optional = true }
|
||||
static_init = { workspace = true }
|
||||
|
||||
const-random = { workspace = true }
|
||||
|
||||
ratatui = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
crossbeam-channel.workspace = true
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
[profile.minimize]
|
||||
inherits = "release"
|
||||
strip = true # Strip symbols from the binary
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
use crate::crypto::feistel_shuffle;
|
||||
|
||||
#[cfg(feature = "counter_shuffle_none")]
|
||||
pub type Counter = NoShuffle;
|
||||
#[cfg(feature = "counter_shuffle_feistel")]
|
||||
pub type Counter = FeistelShuffle;
|
||||
#[cfg(feature = "counter_shuffle_feistel_lcg")]
|
||||
pub type Counter = FeistelLCGShuffle;
|
||||
|
||||
const NONCE16_1: u16 = const_random::const_random!(u16);
|
||||
const NONCE16_2: u16 = const_random::const_random!(u16);
|
||||
const NONCE32: u32 = const_random::const_random!(u32);
|
||||
|
||||
pub struct NoShuffle(u16);
|
||||
|
||||
/// Linear shuffle, no randomization, just a random starting point and step size
|
||||
impl NoShuffle {
|
||||
pub fn new() -> Self {
|
||||
Self(NONCE16_1)
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> u16 {
|
||||
self.0 = self.0.wrapping_add(1);
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Shuffle all 16 bit numbers, an actual shuffle
|
||||
/// But this still stores local values in a linear format
|
||||
pub struct FeistelShuffle(u16, u32);
|
||||
|
||||
impl FeistelShuffle {
|
||||
pub fn new() -> Self {
|
||||
Self(NONCE16_1, NONCE32)
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> u16 {
|
||||
self.0 = self.0.wrapping_add(NONCE16_2);
|
||||
feistel_shuffle(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Linear recursive shuffle,
|
||||
/// feeds back into itself and doesn't store the actual state.
|
||||
/// Harder to decompile
|
||||
pub struct FeistelLCGShuffle {
|
||||
state: u16,
|
||||
a: u16, // Multiplier (must be 1 mod 4)
|
||||
c: u16, // Increment (must be odd)
|
||||
}
|
||||
|
||||
impl FeistelLCGShuffle {
|
||||
pub fn new() -> Self {
|
||||
let seed = NONCE32;
|
||||
let a = (((seed & 0x3FFF) as u16) << 2) | 1;
|
||||
let c = ((seed >> 16) as u16) | 1;
|
||||
Self { state: 0, a, c }
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> u16 {
|
||||
// 1. Advance state using LCG (Guarantees single cycle of 65536)
|
||||
self.state = self.state.wrapping_mul(self.a).wrapping_add(self.c);
|
||||
|
||||
// 2. Apply Feistel shuffle to the state (Adds randomness)
|
||||
feistel_shuffle(self.state, self.a as u32)
|
||||
}
|
||||
}
|
||||
+21
-4
@@ -1,10 +1,27 @@
|
||||
use alloc::string::String;
|
||||
|
||||
mod hash;
|
||||
mod ordering;
|
||||
// TODO: Make this seed dependent on env var;
|
||||
pub const GLOBAL_SEED: u32 = 0xDEAFBEEF;
|
||||
// pub const GLOBAL_NONCE: u32 = {
|
||||
// let time = match u128::from_str_radix(env!("BUILD_TIME"), 10) {
|
||||
// Ok(i) => i,
|
||||
// Err(_) => panic!("Failed to parse BUILD_TIME"),
|
||||
// };
|
||||
|
||||
pub use hash::sha256;
|
||||
pub use ordering::feistel_shuffle;
|
||||
// GLOBAL_SEED ^ (time as u32)
|
||||
// };
|
||||
|
||||
mod feistel;
|
||||
#[allow(dead_code)]
|
||||
mod feistel_state;
|
||||
mod sha256;
|
||||
|
||||
pub use feistel::feistel_shuffle;
|
||||
pub use feistel_state::{Counter, FeistelLCGShuffle, FeistelShuffle, NoShuffle};
|
||||
pub use sha256::sha256;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! hash_256 {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use crate::crypto::{FeistelLCGShuffle, FeistelShuffle, NoShuffle};
|
||||
|
||||
#[test]
|
||||
fn test_linear_shuffle() {
|
||||
let mut seen = [false; 65536];
|
||||
let mut counter = NoShuffle::new();
|
||||
for _ in 0..65535 {
|
||||
let val = counter.next();
|
||||
|
||||
assert!(!seen[val as usize], "Collision detected");
|
||||
|
||||
seen[val as usize] = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_feistel_shuffle() {
|
||||
let mut seen = [false; 65536];
|
||||
let mut counter = FeistelShuffle::new();
|
||||
for _ in 0..65535 {
|
||||
let val = counter.next();
|
||||
|
||||
assert!(!seen[val as usize], "Collision detected");
|
||||
|
||||
seen[val as usize] = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fristel_lcg_shuffle() {
|
||||
let mut seen = [false; 65536];
|
||||
let mut counter = FeistelLCGShuffle::new();
|
||||
for _ in 0..65535 {
|
||||
let val = counter.next();
|
||||
|
||||
assert!(!seen[val as usize], "Collision detected");
|
||||
|
||||
seen[val as usize] = true;
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,11 @@ impl Endpoint {
|
||||
/// reuse an id before the previous route has closed. If every `u16` id is active
|
||||
/// the function panics; that is a hard local resource exhaustion condition, not a
|
||||
/// recoverable packet error.
|
||||
///
|
||||
/// TODO: Reevaluate this method of allocation checking. It can be quite slow
|
||||
pub fn allocate_hook_id(&mut self) -> HookID {
|
||||
for _ in 0..=HookID::MAX {
|
||||
let candidate = self.last_hook;
|
||||
self.last_hook = self.last_hook.wrapping_add(1);
|
||||
let candidate = self.last_hook.next();
|
||||
|
||||
if !self.hooks.contains_key(&candidate) {
|
||||
return candidate;
|
||||
|
||||
@@ -5,15 +5,17 @@ pub use hooks::HookID;
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
|
||||
use crate::protocol::{ConnectionSet, HookMap, Leaf, Packet, Path, RouteMap};
|
||||
use crate::{
|
||||
crypto::Counter,
|
||||
protocol::{ConnectionSet, HookMap, Leaf, Packet, Path, RouteMap},
|
||||
};
|
||||
|
||||
pub struct Endpoint {
|
||||
// This endpoint's identifier
|
||||
pub id: u32,
|
||||
|
||||
// A counter that creates unique hook IDs.
|
||||
// TODO: Randomize the hooks for more obfuscation
|
||||
pub(crate) last_hook: u16,
|
||||
pub(crate) last_hook: Counter,
|
||||
|
||||
// Absolute path for this node. Must be set by some leaf
|
||||
pub path: Path,
|
||||
@@ -36,7 +38,7 @@ impl Endpoint {
|
||||
Self {
|
||||
id,
|
||||
// Init the hook at 0, which will increment
|
||||
last_hook: 0,
|
||||
last_hook: Counter::new(),
|
||||
|
||||
// Set the current path as an empty vec
|
||||
path: Vec::new(),
|
||||
|
||||
Reference in New Issue
Block a user