From 403dddd1b9955b2c3fdb3047a313f478b2d8cb8a Mon Sep 17 00:00:00 2001 From: Herbert Wolverson Date: Fri, 14 Apr 2023 17:52:42 +0000 Subject: [PATCH] PKI for the license server. Replies to public key exchange requests with a server-cluster side public key. 69 bytes, not too heavy. --- .gitignore | 1 + src/rust/Cargo.lock | 2 + .../long_term_stats/license_server/Cargo.toml | 4 +- .../license_server/src/main.rs | 1 + .../long_term_stats/license_server/src/pki.rs | 65 +++++++++++++++++++ .../license_server/src/server.rs | 10 ++- 6 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/rust/long_term_stats/license_server/src/pki.rs diff --git a/.gitignore b/.gitignore index 5d1cdb70..bbdfc58a 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ src/webusers.toml src/lqusers.toml src/dist src/rust/lqos_anonymous_stats_server/anonymous.sqlite +src/rust/long_term_stats/license_server/lqkeys.bin # Ignore Rust build artifacts src/rust/target diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 6e793641..74abe27c 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -1604,9 +1604,11 @@ name = "license_server" version = "0.1.0" dependencies = [ "anyhow", + "dryoc", "env_logger", "log", "lqos_bus", + "once_cell", "pgdb", "serde", "serde_cbor", diff --git a/src/rust/long_term_stats/license_server/Cargo.toml b/src/rust/long_term_stats/license_server/Cargo.toml index a9bda3e3..74d97622 100644 --- a/src/rust/long_term_stats/license_server/Cargo.toml +++ b/src/rust/long_term_stats/license_server/Cargo.toml @@ -11,4 +11,6 @@ log = "0" serde_cbor = "0" # For RFC8949/7409 format C binary objects serde = { version = "1.0", features = ["derive"] } lqos_bus = { path = "../../lqos_bus" } -pgdb = { path = "../pgdb" } \ No newline at end of file +pgdb = { path = "../pgdb" } +dryoc = { version = "0.5", features = ["serde"] } +once_cell = "1" \ No newline at end of file diff --git a/src/rust/long_term_stats/license_server/src/main.rs b/src/rust/long_term_stats/license_server/src/main.rs index 9dde046b..3555f433 100644 --- a/src/rust/long_term_stats/license_server/src/main.rs +++ b/src/rust/long_term_stats/license_server/src/main.rs @@ -1,4 +1,5 @@ mod server; +mod pki; #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/src/rust/long_term_stats/license_server/src/pki.rs b/src/rust/long_term_stats/license_server/src/pki.rs new file mode 100644 index 00000000..6c95a56b --- /dev/null +++ b/src/rust/long_term_stats/license_server/src/pki.rs @@ -0,0 +1,65 @@ +use std::{sync::RwLock, path::Path}; +use dryoc::dryocbox::*; +use lqos_bus::bincode; +use once_cell::sync::Lazy; + +pub(crate) static LIBREQOS_KEYPAIR: Lazy> = Lazy::new(|| RwLock::new(generate_new_keypair())); +const KEY_PATH: &str = "lqkeys.bin"; // Store in the working directory + +pub(crate) fn generate_new_keypair() -> KeyPair { + let path = Path::new(KEY_PATH); + if path.exists() { + if let Ok(bytes) = std::fs::read(path) { + if let Ok(keypair) = bincode::deserialize(&bytes) { + log::info!("Loaded keypair from {}", path.display()); + return keypair; + } + } + } + let keypair = KeyPair::gen(); + let bytes = bincode::serialize(&keypair).unwrap(); + std::fs::write(path, bytes).unwrap(); + log::info!("Generated new keypair and stored it at {}", path.display()); + keypair +} + +#[cfg(test)] +mod test { + use dryoc::dryocbox::*; + use lqos_bus::bincode; + + #[test] + fn test_sealed_box_roundtrip() { + let sender_keypair = KeyPair::gen(); + let recipient_keypair = KeyPair::gen(); + let nonce = Nonce::gen(); + let message = b"Once upon a time, there was a man with a dream."; + let dryocbox = DryocBox::encrypt_to_vecbox( + message, + &nonce, + &recipient_keypair.public_key, + &sender_keypair.secret_key, + ) + .expect("unable to encrypt"); + + let sodium_box = dryocbox.to_vec(); + let dryocbox = DryocBox::from_bytes(&sodium_box).expect("failed to read box"); + let decrypted = dryocbox + .decrypt_to_vec( + &nonce, + &sender_keypair.public_key, + &recipient_keypair.secret_key, + ) + .expect("unable to decrypt"); + + assert_eq!(message, decrypted.as_slice()); + } + + #[test] + fn test_serialize_keypair() { + let keypair = KeyPair::gen(); + let serialized = bincode::serialize(&keypair).unwrap(); + let deserialized : KeyPair = bincode::deserialize(&serialized).unwrap(); + assert_eq!(keypair, deserialized); + } +} \ No newline at end of file diff --git a/src/rust/long_term_stats/license_server/src/server.rs b/src/rust/long_term_stats/license_server/src/server.rs index 90ccdb88..571bc1de 100644 --- a/src/rust/long_term_stats/license_server/src/server.rs +++ b/src/rust/long_term_stats/license_server/src/server.rs @@ -7,6 +7,8 @@ use tokio::{ spawn, }; +use crate::pki::LIBREQOS_KEYPAIR; + pub async fn start() -> anyhow::Result<()> { let listener = TcpListener::bind(":::9126").await?; log::info!("Listening on :::9126"); @@ -33,6 +35,7 @@ pub async fn start() -> anyhow::Result<()> { let bytes = build_reply(&reply); match bytes { Ok(bytes) => { + log::info!("Submitting {} bytes to network", bytes.len()); if let Err(e) = socket.write_all(&bytes).await { log::error!("Write error: {e:?}"); } @@ -108,7 +111,12 @@ async fn check_license( } } LicenseRequest::KeyExchange { node_id, license_key, public_key } => { - Ok(LicenseReply::Denied) + log::info!("Public key exchange requested by {node_id}"); + + // TODO: Associate the node & license key with their key + + let public_key = LIBREQOS_KEYPAIR.read().unwrap().public_key.clone(); + Ok(LicenseReply::MyPublicKey { public_key }) } } }