Initial ChronoSeal release

This commit is contained in:
thakares committed 2026-05-07 21:57:47 +05:30
commit a66debdece
48 files changed
+2362

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
[package]
name = "shared"
version = "0.2.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1" # <- add this line
blake3 = "1"
hex = "0.4"
base64 = "0.22"
rand = "0.8"
ed25519-dalek = { version = "2", features = ["rand_core"] }
+11
View File
@@ -0,0 +1,11 @@
pub const SESSION_ID_LEN: usize = 32;
pub const SALT_LEN: usize = 16;
pub const HEARTBEAT_MIN_INTERVAL_MS: u64 = 12_000;
pub const HEARTBEAT_MAX_INTERVAL_MS: u64 = 25_000;
pub const EXPIRATION_MINUTES: i64 = 30;
pub const RATE_LIMIT_COUNT: u32 = 5;
pub const RATE_LIMIT_WINDOW_SECS: u64 = 10;
pub const MAX_TIMESTAMP_DRIFT_MS: i64 = 30_000;
pub const MIN_MOUSE_TOTAL_DIST: f64 = 10.0;
pub const MAX_MOUSE_AVG_SPEED: f64 = 2.0; // px/ms
pub const MIN_PAUSE_COUNT: u32 = 1;
+42
View File
@@ -0,0 +1,42 @@
use blake3::Hasher;
use crate::protocol::{EntropyData, StackState};
/// Initial hash for a brand-new session: Blake3(session_id || pub_key || salt)
pub fn initial_hash(session_id: &str, pub_key: &[u8], salt: &[u8]) -> Vec<u8> {
let mut h = Hasher::new();
h.update(session_id.as_bytes());
h.update(pub_key);
h.update(salt);
h.finalize().as_bytes().to_vec()
}
/// Next hash in the chain: Blake3 with the salt mixed in (no keyed mode needed)
pub fn next_chain_hash(
prev_hash: &[u8],
timestamp: u64,
entropy: &EntropyData,
stack: &StackState,
salt: &[u8],
) -> Vec<u8> {
let entropy_json = serde_json::to_string(entropy).unwrap();
let stack_json = serde_json::to_string(stack).unwrap();
let entropy_hash = blake3::hash(entropy_json.as_bytes());
let stack_hash = blake3::hash(stack_json.as_bytes());
let mut h = Hasher::new();
// Mix the salt into the hash state
h.update(salt);
h.update(prev_hash);
h.update(&timestamp.to_le_bytes());
h.update(entropy_hash.as_bytes());
h.update(stack_hash.as_bytes());
h.finalize().as_bytes().to_vec()
}
/// Hash of all stack items for VM HASH opcode
pub fn hash_stack(stack: &[u32]) -> u32 {
let data: Vec<u8> = stack.iter().flat_map(|x| x.to_le_bytes()).collect();
let hash = blake3::hash(&data);
u32::from_le_bytes(hash.as_bytes()[..4].try_into().unwrap())
}
+3
View File
@@ -0,0 +1,3 @@
pub mod constants;
pub mod hashing;
pub mod protocol;
+62
View File
@@ -0,0 +1,62 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
pub struct InitRequest {
pub public_key: String,
}
#[derive(Serialize)]
pub struct InitResponse {
pub session_id: String,
pub salt: String,
pub opcodes_b64: String,
pub initial_hash: String,
pub expires_at: u64,
}
#[derive(Deserialize, Serialize)]
pub struct HeartbeatRequest {
pub session_id: String,
pub prev_hash: String,
pub timestamp: u64,
pub entropy_data: EntropyData,
pub stack_state: StackState,
pub fingerprint: Fingerprint,
pub signature: String,
}
#[derive(Serialize)]
pub struct HeartbeatResponse {
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_salt: Option<String>,
}
#[derive(Deserialize, Serialize)]
pub struct Fingerprint {
#[serde(rename = "aspectRatio")]
pub aspect_ratio: String,
#[serde(rename = "devicePixelRatio")]
pub device_pixel_ratio: String,
#[serde(rename = "hardwareConcurrency")]
pub hardware_concurrency: u32,
}
#[derive(Deserialize, Serialize)]
pub struct EntropyData {
pub events: Vec<MouseEvent>,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct MouseEvent {
pub x: f64,
pub y: f64,
#[serde(rename = "t")]
pub timestamp_ms: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StackState {
pub stack: Vec<u32>,
pub ip: u16,
}