initial commit: added very basic symmetric key service api
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+7
@@ -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"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "symmetric-key-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
+134
@@ -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<String> },
|
||||||
|
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<Mutex<HashMap<SessionId,KeyEntry>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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<KeyResponse, KmeError> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user