From 8822629dcdb2751f7b05d39d14675f9372823fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Pr=C3=A9vost?= Date: Tue, 6 Feb 2024 11:38:44 +0100 Subject: [PATCH] Use JSON config file instead of arg command line --- config_kme1.json | 76 ++++++++++++++++ src/config/mod.rs | 73 ++++++++++++++++ src/lib.rs | 4 + src/main.rs | 131 ++++------------------------ src/qkd_manager/config_extractor.rs | 96 ++++++++++++++++++++ src/qkd_manager/key_handler.rs | 3 + src/qkd_manager/mod.rs | 18 ++++ 7 files changed, 289 insertions(+), 112 deletions(-) create mode 100644 config_kme1.json create mode 100644 src/config/mod.rs create mode 100644 src/qkd_manager/config_extractor.rs diff --git a/config_kme1.json b/config_kme1.json new file mode 100644 index 0000000..b8c115f --- /dev/null +++ b/config_kme1.json @@ -0,0 +1,76 @@ +{ + "this_kme": { + "id": 1, + "sqlite_db_path": ":memory:", + "https_listen_address": "127.0.0.1:3000", + "https_ca_client_cert_path": "certs/CA-zone1.crt", + "https_server_cert_path": "certs/kme1.crt", + "https_server_key_path": "certs/kme1.key" + }, + "other_kmes": [ + { + "id": 1, + "key_directory_to_watch": "raw_keys", + "ip_address": "127.0.0.1" + }, + { + "id": 2, + "key_directory_to_watch": "raw_keys", + "ip_address": "127.0.0.1" + } + ], + "saes": [ + { + "id": 1, + "kme_id": 1, + "https_client_certificate_serial": [ + 112, + 244, + 79, + 86, + 12, + 63, + 39, + 212, + 178, + 17, + 164, + 120, + 19, + 175, + 208, + 60, + 3, + 129, + 59, + 142 + ] + }, + { + "id": 2, + "kme_id": 1, + "https_client_certificate_serial": [ + 112, + 244, + 79, + 86, + 12, + 63, + 39, + 212, + 178, + 17, + 164, + 120, + 19, + 175, + 208, + 60, + 3, + 129, + 59, + 146 + ] + } + ] +} \ No newline at end of file diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..6801817 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,73 @@ +//! Configuration module, contains structs used to deserialize JSON configuration and other material extraction functions + +use std::io; +use serde::{Deserialize, Serialize}; +use crate::{io_err, KmeId, SaeClientCertSerial, SaeId}; + +/// Whole KME config, to be extracted from JSON +#[derive(Serialize, Deserialize, Debug)] +pub struct Config { + /// Config for this specific KME, including its ID and paths to certificates + #[serde(rename = "this_kme")] + pub this_kme_config: ThisKmeConfig, + /// Configs for other KMEs, including their IDs and paths to directories to watch for new keys + #[serde(rename = "other_kmes")] + pub(crate) other_kme_configs: Vec, + /// Configs for SAEs, including their IDs, KMEs they are associated with and optional client certificate serials, if SAEs belong to this KME + #[serde(rename = "saes")] + pub(crate) sae_configs: Vec +} + +impl Config { + /// Extract configuration from JSON file + pub fn from_json_path(json_config_file_path: &str) -> Result { + let config: Self = match std::fs::read_to_string(json_config_file_path) { + Ok(json) => match serde_json::from_str(&json) { + Ok(config) => config, + Err(_) => return Err(io_err("Error deserializing JSON")) + }, + Err(_) => return Err(io_err("Error reading JSON file")) + }; + Ok(config) + } +} + +/// Config for this specific KME, including its ID and paths to certificates +#[derive(Serialize, Deserialize, Debug)] +pub struct ThisKmeConfig { + /// ID of this KME, in the QKD network + pub(crate) id: KmeId, + /// Path to SQLite database file, used to store keys, certificates and other data + /// You can use `:memory:` to use in-memory database + pub(crate) sqlite_db_path: String, + /// Address to listen for HTTPS connections + pub https_listen_address: String, + /// Server certificate authority certificate path, used to authenticate client SAEs + pub https_ca_client_cert_path: String, + /// Server HTTPS certificate path + pub https_server_cert_path: String, + /// Server HTTPS private key path + pub https_server_key_path: String +} + +/// Configs for other KMEs, including their IDs and paths to directories to watch for new keys +#[derive(Serialize, Deserialize, Debug)] +pub struct OtherKmeConfig { + /// ID of the other KME, in the QKD network + pub(crate) id: KmeId, + /// Path to directory to read and watch for new keys, files must have [crate::QKD_KEY_FILE_EXTENSION](crate::QKD_KEY_FILE_EXTENSION) extension + pub(crate) key_directory_to_watch: String, + /// IP address of the other KME, used to send keys to it using "classical channel" + pub(crate) ip_address: String, +} + +/// Config for specific SAE: its ID, KME ID and optional client certificate serial +#[derive(Serialize, Deserialize, Debug)] +pub struct SaeConfig { + /// ID of the SAE in QKD network + pub(crate) id: SaeId, + /// ID of the KME this SAE belongs to + pub(crate) kme_id: KmeId, + /// Client certificate serial number, used to authenticate SAEs to this KME if it belongs to, None otherwise + pub(crate) https_client_certificate_serial: Option +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index be3ad7b..bd08d0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ use std::io; pub mod server; pub mod routes; pub mod qkd_manager; +pub mod config; /// Cast a string to an io::Error @@ -53,6 +54,9 @@ pub const CLIENT_CERT_SERIAL_SIZE_BYTES: usize = 20; /// Location of the SQLite database file used by the KME to store keys, use ":memory:" for in-memory database pub const MEMORY_SQLITE_DB_PATH: &'static str = ":memory:"; +/// File extension for newly exchanged QKD keys +pub const QKD_KEY_FILE_EXTENSION: &'static str = "cor"; + /// The type of SAE ID pub type SaeId = i64; diff --git a/src/main.rs b/src/main.rs index 4ad34aa..4ddbd7f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,130 +1,37 @@ -use std::io::{BufReader, Read}; -use std::path::Path; -use std::sync::Arc; use log::error; -use clap::Parser; -use notify::{EventKind, RecursiveMode, Watcher}; -use notify::event::{AccessKind, AccessMode}; -use qkd_kme_server::qkd_manager::{PreInitQkdKeyWrapper, QkdManager}; +use qkd_kme_server::qkd_manager::QkdManager; use qkd_kme_server::routes::QKDKMERoutesV1; #[tokio::main] async fn main() { simple_logger::SimpleLogger::new().init().unwrap(); - let args = Args::parse(); - println!("{:?}", args); - - - let server = qkd_kme_server::server::Server { - listen_addr: "127.0.0.1:3000".to_string(), - ca_client_cert_path: "certs/CA-zone1.crt".to_string(), - server_cert_path: "certs/kme1.crt".to_string(), - server_key_path: "certs/kme1.key".to_string(), - }; - - let qkd_manager = Arc::new(QkdManager::new(qkd_kme_server::MEMORY_SQLITE_DB_PATH, args.this_kme_id)); - if qkd_manager.add_sae(1, - 1, - &Some([0x70, 0xf4, 0x4f, 0x56, 0x0c, 0x3f, 0x27, 0xd4, 0xb2, 0x11, 0xa4, 0x78, 0x13, 0xaf, 0xd0, 0x3c, 0x03, 0x81, 0x3b, 0x8e]) - ).is_err() { - error!("Error adding SAE to QKD manager"); - return; - } - if qkd_manager.add_sae(2, - 1, - &Some([0x70, 0xf4, 0x4f, 0x56, 0x0c, 0x3f, 0x27, 0xd4, 0xb2, 0x11, 0xa4, 0x78, 0x13, 0xaf, 0xd0, 0x3c, 0x03, 0x81, 0x3b, 0x92]) - ).is_err() { - error!("Error adding SAE to QKD manager"); + if std::env::args().len() != 2 { + eprintln!("Usage: {} ", std::env::args().nth(0).unwrap()); return; } - let mut watchers: Vec = Vec::new(); - - for kme_dir in args.dirs_to_watch_other_kme_ids { - extract_all_keys_from_dir(&kme_dir.dir, kme_dir.kme_id, &qkd_manager); - let kme_id = kme_dir.kme_id; - let qkd_manager = Arc::clone(&qkd_manager); - watchers.push(match notify::recommended_watcher(move |res: Result| { - match res { - Ok(event) => { - if let EventKind::Access(AccessKind::Close(AccessMode::Write)) = event.kind { - extract_all_keys_from_file(&event.paths[0].to_str().unwrap(), kme_id, &qkd_manager); - } - } - Err(e) => { - println!("Watch error: {:?}", e); - return; - } - } - }) { - Ok(watcher) => watcher, - Err(e) => { - error!("Error creating watcher: {:?}", e); - return; - } - }); - if watchers.iter_mut().last().unwrap().watch(Path::new(&kme_dir.dir), RecursiveMode::NonRecursive).is_err() { - error!("Error watching directory: {:?}", kme_dir.dir); + let config = match qkd_kme_server::config::Config::from_json_path(&std::env::args().nth(1).unwrap()) { + Ok(config) => config, + Err(e) => { + eprintln!("Error reading config: {:?}", e); return; } - } + }; + + let server = qkd_kme_server::server::Server { + listen_addr: config.this_kme_config.https_listen_address.clone(), + ca_client_cert_path: config.this_kme_config.https_ca_client_cert_path.clone(), + server_cert_path: config.this_kme_config.https_server_cert_path.clone(), + server_key_path: config.this_kme_config.https_server_key_path.clone(), + }; + + let qkd_manager= QkdManager::from_config(&config); + println!("{:?}", qkd_manager.is_err()); + let qkd_manager = qkd_manager.unwrap(); if server.run::(&qkd_manager).await.is_err() { error!("Error running HTTP server"); return; } -} - -#[derive(Parser, Debug)] -struct Args { - #[arg(long)] - this_kme_id: i64, - #[arg(long("kme_id,dir_watch"))] - dirs_to_watch_other_kme_ids: Vec, -} - -#[derive(Debug, Clone)] -struct DirWatchOtherKmesArgs { - dir: String, - kme_id: i64, -} - -impl std::str::FromStr for DirWatchOtherKmesArgs { - type Err = std::io::Error; - fn from_str(s: &str) -> Result { - let kme_id_and_dir = s.split(",").collect::>(); - if kme_id_and_dir.len() != 2 { - return Err(std::io::Error::other("Invalid format")); - } - let kme_id = i64::from_str(kme_id_and_dir[0]).map_err(|_| std::io::Error::other("Invalid format"))?; - Ok(Self { - dir: kme_id_and_dir[1].to_string(), - kme_id, - }) - } -} - -// TODO: move to QKD manager struct -fn extract_all_keys_from_file(file_path: &str, other_kme_id: i64, qkd_manager: &QkdManager) { - let file = std::fs::File::open(file_path).unwrap(); - let mut reader = BufReader::with_capacity(32, file); - let mut buffer = [0; 32]; - while let Ok(_) = reader.read_exact(&mut buffer) { - let qkd_key = PreInitQkdKeyWrapper::new( - other_kme_id, - &buffer, - ).unwrap(); - qkd_manager.add_pre_init_qkd_key(qkd_key).unwrap(); - } -} - -fn extract_all_keys_from_dir(dir_path: &str, other_kme_id: i64, qkd_manager: &QkdManager) { - let paths = std::fs::read_dir(dir_path).unwrap(); - for path in paths { - let path = path.unwrap().path(); - if path.is_file() { - extract_all_keys_from_file(path.to_str().unwrap(), other_kme_id, qkd_manager); - } - } } \ No newline at end of file diff --git a/src/qkd_manager/config_extractor.rs b/src/qkd_manager/config_extractor.rs new file mode 100644 index 0000000..ea53aa2 --- /dev/null +++ b/src/qkd_manager/config_extractor.rs @@ -0,0 +1,96 @@ +use std::io; +use std::io::{BufReader, Read}; +use std::path::Path; +use std::sync::Arc; +use notify::event::{AccessKind, AccessMode}; +use notify::{EventKind, RecursiveMode, Watcher}; +use crate::config::Config; +use crate::io_err; +use crate::qkd_manager::{PreInitQkdKeyWrapper, QkdManager}; + +pub(super) struct ConfigExtractor {} + +impl ConfigExtractor { + pub(super) fn extract_config_to_qkd_manager(config: &Config) -> Result, io::Error> { + let qkd_manager = Arc::new(QkdManager::new(&config.this_kme_config.sqlite_db_path, config.this_kme_config.id)); + Self::extract_all_saes(Arc::clone(&qkd_manager), config)?; + Self::extract_other_kmes_and_keys(Arc::clone(&qkd_manager), config)?; + Ok(qkd_manager) + } + + fn extract_other_kmes_and_keys(qkd_manager: Arc, config: &Config) -> Result<(), io::Error> { + for other_kme_config in &config.other_kme_configs { + let kme_id = other_kme_config.id; + let kme_keys_dir = other_kme_config.key_directory_to_watch.as_str(); + let mut dir_watchers = qkd_manager.dir_watcher.lock().unwrap(); + let qkd_manager = Arc::clone(&qkd_manager); + Self::extract_all_keys_from_dir(Arc::clone(&qkd_manager), kme_keys_dir, other_kme_config.id); + + dir_watchers.push(match notify::recommended_watcher(move |res: Result| { + match res { + Ok(event) => { + if let EventKind::Access(AccessKind::Close(AccessMode::Write)) = event.kind { + if Self::check_file_extension_qkd_keys(event.paths[0].to_str().unwrap()) { + Self::extract_all_keys_from_file(Arc::clone(&qkd_manager), &event.paths[0].to_str().unwrap(), kme_id); + } + } + } + Err(e) => { + println!("Watch error: {:?}", e); + return; + } + } + }) { + Ok(watcher) => watcher, + Err(e) => { + return Err(io_err(&format!("Error creating watcher: {:?}", e))); + } + }); + if dir_watchers.iter_mut().last().unwrap().watch(Path::new(kme_keys_dir), RecursiveMode::NonRecursive).is_err() { + return Err(io_err(&format!("Error watching directory: {:?}", kme_keys_dir))); + } + } + Ok(()) + } + + fn extract_all_saes(qkd_manager: Arc, config: &Config) -> Result<(), io::Error> { + for sae_config in &config.sae_configs { + qkd_manager.add_sae(sae_config.id, sae_config.kme_id, &sae_config.https_client_certificate_serial) + .map_err(|e| + io_err(&format!("Cannot add SAE config: {:?}", e)) + )?; + } + Ok(()) + } + + + fn extract_all_keys_from_file(qkd_manager: Arc, file_path: &str, other_kme_id: i64) { + let file = std::fs::File::open(file_path).unwrap(); + let mut reader = BufReader::with_capacity(32, file); + let mut buffer = [0; 32]; + while let Ok(_) = reader.read_exact(&mut buffer) { + let qkd_key = PreInitQkdKeyWrapper::new( + other_kme_id, + &buffer, + ).unwrap(); + qkd_manager.add_pre_init_qkd_key(qkd_key).unwrap(); + } + } + + fn extract_all_keys_from_dir(qkd_manager: Arc, dir_path: &str, other_kme_id: i64) { + let paths = std::fs::read_dir(dir_path).unwrap(); + for path in paths { + let path = path.unwrap().path(); + if path.is_file() { + if Self::check_file_extension_qkd_keys(path.to_str().unwrap()) { + Self::extract_all_keys_from_file(Arc::clone(&qkd_manager), path.to_str().unwrap(), other_kme_id); + } + } + } + } + + fn check_file_extension_qkd_keys(file_path: &str) -> bool { + let file_ext = Path::new(file_path).extension(); + file_ext.is_some() && file_ext.unwrap() == crate::QKD_KEY_FILE_EXTENSION + } +} \ No newline at end of file diff --git a/src/qkd_manager/key_handler.rs b/src/qkd_manager/key_handler.rs index 8e59365..9ddef37 100644 --- a/src/qkd_manager/key_handler.rs +++ b/src/qkd_manager/key_handler.rs @@ -270,6 +270,9 @@ impl KeyHandler { })?; if origin_kme_id != target_kme_id { // send key to other KME TODO + // We must ensure: + // - other KME is authenticated + // - other SAE belongs to other KME } self.delete_pre_init_key_with_id(id).map_err(|_| { diff --git a/src/qkd_manager/mod.rs b/src/qkd_manager/mod.rs index c290591..e73af0d 100644 --- a/src/qkd_manager/mod.rs +++ b/src/qkd_manager/mod.rs @@ -4,8 +4,10 @@ mod key_handler; pub(crate) mod http_response_obj; pub(crate) mod http_request_obj; mod router; +mod config_extractor; use std::{io, thread}; +use std::sync::{Arc, Mutex}; use log::error; use sha1::Digest; use crate::qkd_manager::http_response_obj::ResponseQkdKeysList; @@ -19,6 +21,8 @@ pub struct QkdManager { command_tx: crossbeam_channel::Sender, /// Channel to receive responses from the key handler response_rx: crossbeam_channel::Receiver, + /// Directory watchers for other KME keys, placed here because watch stops when dropped + pub(crate) dir_watcher: Arc>> } impl QkdManager { @@ -46,12 +50,26 @@ impl QkdManager { }; key_handler.run(); }); + + let dir_watcher = Arc::new(Mutex::new(Vec::new())); + Self { command_tx, response_rx, + dir_watcher } } + /// Create a new QKD manager handler from a configuration + /// # Arguments + /// * `config` - The configuration to use to create the QKD manager + /// # Returns + /// A new QKD manager handler if the configuration is valid, an error otherwise + pub fn from_config(config: &crate::config::Config) -> Result, io::Error> { + let qkd_manager = config_extractor::ConfigExtractor::extract_config_to_qkd_manager(config)?; + Ok(qkd_manager) + } + /// Add a new QKD key to the database /// # Arguments /// * `key` - The QKD key to add (key + origin SAE ID + target SAE ID)