commit 1925deff91c043c6c06f823239cac447c3e0e9f0 Author: Ed Guloien Date: Wed May 27 22:59:18 2026 -0400 initial commit: added very basic symmetric key service api diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d8570c5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "symmetric-key-service" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..70b6926 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "symmetric-key-service" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0d71e81 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,134 @@ +// Design a Symmetric Key Distribution Service" +// +// You are building a Key Management Entity (KME) +// a service responsible for generating, storing, and distributing pre-shared +// symmetric keys to authenticated clients (e.g. network gateways, endpoints). +// Design and implement a core slice of this system in Rust. +// +// Requirements: +// Clients authenticate and request a key by providing a session_id and peer_id +// The KME returns a fresh 256-bit key, ensuring both sides of a session receive the same key +// Keys are single-use: once consumed, they cannot be re-issued +// The service must handle concurrent requests safely +// Keys not consumed within 60 seconds should expire +// +// Deliverables: +// * A Rust implementation of the core KME logic +// (not necessarily a full HTTP server — a library interface is fine) +// * A brief design doc covering: your data structures, concurrency strategy, +// and how you'd extend this to multiple KME nodes + +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; + +enum KeyEntryState { + Active { retrieved_by: Vec }, + Consumed, +} + +#[derive(Debug, PartialEq)] +enum KmeError { + KeyConsumed, + KeyExpired, +} + +struct KeyEntry { + key: [u8; 32], // 256 bits + creation: Instant, + state: KeyEntryState, +} + +#[derive(Debug, PartialEq)] +struct KeyResponse { + pub key_id: String, // stable identifier for this key + pub key: [u8; 32], +} + +type SessionId = [u8; 16]; // similar to uuid +type PeerId = u32; +type Peer = (SessionId, PeerId); + +struct Auth { + user_count: u32, + session_count: u8, +} + +impl Auth { + pub fn login(&mut self) -> Result<(SessionId, PeerId), String>{ + self.user_count+=1; + self.session_count+=1; + // give out two session ids in a row to simulate functional auth service + // where two peers who want to communicate will be given the same session id + let session_id = self.session_count/2; + Ok(([session_id;16], self.user_count)) + } +} + +struct Kme { + keys: Arc>>, +} + + +impl Kme { + fn new() -> Self { + Self { keys: Arc::new(Mutex::new(HashMap::new())) } + } + + // todo: service should handle concurrent requests safely + pub fn request_key(&self, peer: Peer) -> Result { + let (session_id, _peer_id) = peer; + let mut keys = self.keys.lock().unwrap(); + + // keys should be single use + if let Some(entry) = keys.get_mut(&session_id) { + let diff = Instant::now() - entry.creation; + // keys will timeout + if diff > Duration::from_secs(1) { // 1 second for feasible testing + return Err(KmeError::KeyExpired); + } + if let KeyEntryState::Consumed = entry.state { + // third peer: key already consumed + return Err(KmeError::KeyConsumed); + } + // second peer: return the same key + entry.state = KeyEntryState::Consumed; + // todo: update retrieved_by as well? why needed? + Ok(KeyResponse { key_id: String::new(), key: entry.key }) + } else { + // first peer: generate and store a new key + let key = [0u8; 32]; // TODO: generate real key bytes + keys.insert(session_id, KeyEntry { + key, + creation: Instant::now(), + state: KeyEntryState::Active { retrieved_by: vec![] }, + }); + Ok(KeyResponse { key_id: String::new(), key }) + } + } +} + +fn main() { + let mut auth = Auth{ user_count: 0, session_count: 1}; + let first_peer = auth.login().unwrap(); // assume fine for now + let second_peer = auth.login().unwrap(); // assume fine for now + + let kme = Kme::new(); + let first_key = kme.request_key((first_peer.0, second_peer.1)).unwrap(); + let second_key = kme.request_key((second_peer.0, first_peer.1)).unwrap(); + assert_eq!(first_key, second_key); + + let dupe_attempt = kme.request_key((second_peer.0, first_peer.1)).unwrap_err(); + assert_eq!(dupe_attempt, KmeError::KeyConsumed); + + let third_peer = auth.login().unwrap(); + // third_peer somehow hijacks second peer's session_id + let third_key = kme.request_key((second_peer.0, third_peer.1)).unwrap_err(); + assert_eq!(third_key, KmeError::KeyConsumed); + + let forth_peer = auth.login().unwrap(); + let _third_key = kme.request_key((third_peer.0, forth_peer.1)).unwrap(); + std::thread::sleep(std::time::Duration::from_secs(2)); + let forth_key = kme.request_key((forth_peer.0, third_peer.1)).unwrap_err(); + assert_eq!(forth_key, KmeError::KeyExpired); +}