succesfully injecting datastore into webserver
This commit is contained in:
		
							parent
							
								
									32fcfcb385
								
							
						
					
					
						commit
						4f2cec3fa7
					
				
							
								
								
									
										97
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										97
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -470,6 +470,16 @@ dependencies = [ | |||||||
|  "windows-sys 0.52.0", |  "windows-sys 0.52.0", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "etag" | ||||||
|  | version = "4.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "4b3d0661a2ccddc26cba0b834e9b717959ed6fdd76c7129ee159c170a875bf44" | ||||||
|  | dependencies = [ | ||||||
|  |  "str-buf", | ||||||
|  |  "xxhash-rust", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "fastrand" | name = "fastrand" | ||||||
| version = "2.1.0" | version = "2.1.0" | ||||||
| @ -669,6 +679,7 @@ dependencies = [ | |||||||
| name = "hacker-challenge" | name = "hacker-challenge" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "anyhow", | ||||||
|  "ed25519-dalek", |  "ed25519-dalek", | ||||||
|  "futures", |  "futures", | ||||||
|  "hex", |  "hex", | ||||||
| @ -1677,6 +1688,7 @@ dependencies = [ | |||||||
|  "salvo-jwt-auth", |  "salvo-jwt-auth", | ||||||
|  "salvo-proxy", |  "salvo-proxy", | ||||||
|  "salvo_core", |  "salvo_core", | ||||||
|  |  "salvo_extra", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -1772,6 +1784,26 @@ dependencies = [ | |||||||
|  "tracing", |  "tracing", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "salvo_extra" | ||||||
|  | version = "0.70.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "11a1d577b798b1fd642bc8d3a80b2bb9299c9236a0a2e072c597099daee28557" | ||||||
|  | dependencies = [ | ||||||
|  |  "base64 0.22.1", | ||||||
|  |  "etag", | ||||||
|  |  "futures-util", | ||||||
|  |  "hyper", | ||||||
|  |  "pin-project", | ||||||
|  |  "salvo_core", | ||||||
|  |  "serde", | ||||||
|  |  "serde_json", | ||||||
|  |  "tokio", | ||||||
|  |  "tokio-tungstenite", | ||||||
|  |  "tracing", | ||||||
|  |  "ulid", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "salvo_macros" | name = "salvo_macros" | ||||||
| version = "0.70.0" | version = "0.70.0" | ||||||
| @ -1976,6 +2008,12 @@ dependencies = [ | |||||||
|  "der", |  "der", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "str-buf" | ||||||
|  | version = "3.0.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0ceb97b7225c713c2fd4db0153cb6b3cab244eb37900c3f634ed4d43310d8c34" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "subtle" | name = "subtle" | ||||||
| version = "2.6.1" | version = "2.6.1" | ||||||
| @ -2198,6 +2236,18 @@ dependencies = [ | |||||||
|  "tokio", |  "tokio", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "tokio-tungstenite" | ||||||
|  | version = "0.23.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" | ||||||
|  | dependencies = [ | ||||||
|  |  "futures-util", | ||||||
|  |  "log", | ||||||
|  |  "tokio", | ||||||
|  |  "tungstenite", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tokio-util" | name = "tokio-util" | ||||||
| version = "0.7.11" | version = "0.7.11" | ||||||
| @ -2340,12 +2390,37 @@ version = "0.2.5" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "tungstenite" | ||||||
|  | version = "0.23.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" | ||||||
|  | dependencies = [ | ||||||
|  |  "byteorder", | ||||||
|  |  "bytes", | ||||||
|  |  "log", | ||||||
|  |  "rand", | ||||||
|  |  "thiserror", | ||||||
|  |  "utf-8", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "typenum" | name = "typenum" | ||||||
| version = "1.17.0" | version = "1.17.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ulid" | ||||||
|  | version = "1.1.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" | ||||||
|  | dependencies = [ | ||||||
|  |  "getrandom", | ||||||
|  |  "rand", | ||||||
|  |  "web-time", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicase" | name = "unicase" | ||||||
| version = "2.7.0" | version = "2.7.0" | ||||||
| @ -2409,6 +2484,12 @@ dependencies = [ | |||||||
|  "percent-encoding", |  "percent-encoding", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "utf-8" | ||||||
|  | version = "0.7.6" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "vcpkg" | name = "vcpkg" | ||||||
| version = "0.2.15" | version = "0.2.15" | ||||||
| @ -2526,6 +2607,16 @@ dependencies = [ | |||||||
|  "wasm-bindgen", |  "wasm-bindgen", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "web-time" | ||||||
|  | version = "1.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" | ||||||
|  | dependencies = [ | ||||||
|  |  "js-sys", | ||||||
|  |  "wasm-bindgen", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-sys" | name = "windows-sys" | ||||||
| version = "0.48.0" | version = "0.48.0" | ||||||
| @ -2699,6 +2790,12 @@ version = "0.8.21" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" | checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "xxhash-rust" | ||||||
|  | version = "0.8.12" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "zerocopy" | name = "zerocopy" | ||||||
| version = "0.7.35" | version = "0.7.35" | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ version = "0.1.0" | |||||||
| edition = "2021" | edition = "2021" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
|  | anyhow = "1.0.86" | ||||||
| ed25519-dalek = { version = "2.1.1", features = ["rand_core", "serde"] } | ed25519-dalek = { version = "2.1.1", features = ["rand_core", "serde"] } | ||||||
| futures = "0.3.30" | futures = "0.3.30" | ||||||
| hex = "0.4.3" | hex = "0.4.3" | ||||||
| @ -11,7 +12,7 @@ once_cell = "1.19.0" | |||||||
| prost = "0.13.1" | prost = "0.13.1" | ||||||
| prost-types = "0.13.1" | prost-types = "0.13.1" | ||||||
| rand = "0.8.5" | rand = "0.8.5" | ||||||
| salvo = "0.70.0" | salvo = { version = "0.70.0", features = ["affix"] } | ||||||
| tabled = "0.16.0" | tabled = "0.16.0" | ||||||
| tokio = { version = "1.39.2", features = ["macros"] } | tokio = { version = "1.39.2", features = ["macros"] } | ||||||
| tonic = "0.12.1" | tonic = "0.12.1" | ||||||
|  | |||||||
| @ -2,25 +2,14 @@ syntax = "proto3"; | |||||||
| package challenge; | package challenge; | ||||||
| 
 | 
 | ||||||
| import "google/protobuf/timestamp.proto"; | import "google/protobuf/timestamp.proto"; | ||||||
| import "google/protobuf/empty.proto"; | 
 | ||||||
|  | message NodeUpdate { | ||||||
|  |    string ip = 1; | ||||||
|  |    string keypair = 2; | ||||||
|  |    google.protobuf.Timestamp updated_at = 3; | ||||||
|  |    bool online = 4; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| service KeyDistribution { | service KeyDistribution { | ||||||
|     rpc UpdateKey (UpdateKeyReq) returns (google.protobuf.Empty); |     rpc UpdateStreaming (stream NodeUpdate) returns (stream NodeUpdate); | ||||||
|     rpc UpdateNode (UpdateNodeReq) returns (google.protobuf.Empty); |  | ||||||
|     rpc RemoveNode (RemoveNodeReq) returns (google.protobuf.Empty); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| message UpdateKeyReq { |  | ||||||
|    string keypair = 1; |  | ||||||
|    google.protobuf.Timestamp updated_at = 2; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| message UpdateNodeReq { |  | ||||||
|    string keypair = 1; |  | ||||||
|    google.protobuf.Timestamp updated_at = 2; |  | ||||||
|    string ip = 3; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| message RemoveNodeReq { |  | ||||||
|    string ip = 1; |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										141
									
								
								src/database.rs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										141
									
								
								src/database.rs
									
									
									
									
									
								
							| @ -1,141 +0,0 @@ | |||||||
| #![allow(dead_code)] |  | ||||||
| use ed25519_dalek::{Signer, SigningKey, VerifyingKey}; |  | ||||||
| use once_cell::sync::Lazy; |  | ||||||
| use rand::rngs::OsRng; |  | ||||||
| use std::collections::HashMap; |  | ||||||
| use std::sync::Mutex; |  | ||||||
| use std::thread; |  | ||||||
| use std::time::Duration; |  | ||||||
| use std::time::SystemTime; |  | ||||||
| use tabled::{Table, Tabled}; |  | ||||||
| 
 |  | ||||||
| pub enum SigningError { |  | ||||||
|     CorruptedKey, |  | ||||||
|     KeyNotFound, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl std::fmt::Display for SigningError { |  | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |  | ||||||
|         let error_message = match self { |  | ||||||
|             SigningError::CorruptedKey => "The public key is corrupted", |  | ||||||
|             SigningError::KeyNotFound => "Did not find the public key", |  | ||||||
|         }; |  | ||||||
|         write!(f, "{}", error_message) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<hex::FromHexError> for SigningError { |  | ||||||
|     fn from(_: hex::FromHexError) -> Self { |  | ||||||
|         Self::CorruptedKey |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<ed25519_dalek::ed25519::Error> for SigningError { |  | ||||||
|     fn from(_: ed25519_dalek::ed25519::Error) -> Self { |  | ||||||
|         Self::CorruptedKey |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<std::array::TryFromSliceError> for SigningError { |  | ||||||
|     fn from(_: std::array::TryFromSliceError) -> Self { |  | ||||||
|         Self::CorruptedKey |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone)] |  | ||||||
| pub struct NodeInfo { |  | ||||||
|     pub pubkey: VerifyingKey, |  | ||||||
|     pub updated_at: SystemTime, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static NODES: Lazy<Mutex<HashMap<String, NodeInfo>>> = Lazy::new(|| Mutex::new(HashMap::new())); |  | ||||||
| 
 |  | ||||||
| static KEYS: Lazy<Mutex<HashMap<VerifyingKey, SigningKey>>> = |  | ||||||
|     Lazy::new(|| Mutex::new(HashMap::new())); |  | ||||||
| 
 |  | ||||||
| pub fn add_key(pubkey: VerifyingKey, privkey: SigningKey) { |  | ||||||
|     let mut keys = KEYS.lock().unwrap(); |  | ||||||
|     keys.insert(pubkey, privkey); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn remove_key(pubkey: &VerifyingKey) { |  | ||||||
|     let mut keys = KEYS.lock().unwrap(); |  | ||||||
|     keys.remove(pubkey); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn get_privkey(pubkey: &VerifyingKey) -> Option<SigningKey> { |  | ||||||
|     let keys = KEYS.lock().unwrap(); |  | ||||||
|     keys.get(pubkey).cloned() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn sign_message_with_key(pubkey: &str, message: &str) -> Result<String, SigningError> { |  | ||||||
|     // Parse the hex string into a VerifyingKey
 |  | ||||||
|     let key_bytes = hex::decode(pubkey)?; |  | ||||||
|     let pubkey = VerifyingKey::from_bytes(&key_bytes.as_slice().try_into()?)?; |  | ||||||
| 
 |  | ||||||
| // Lock the hashmap and try to get the SigningKey
 |  | ||||||
|     let key_store = KEYS.lock().unwrap(); |  | ||||||
|     let signing_key = match key_store.get(&pubkey) { |  | ||||||
|         Some(k) => k, |  | ||||||
|         None => return Err(SigningError::KeyNotFound), |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     // TODO: check if to_bytes returns the signature in a format that people can verify from bash
 |  | ||||||
|     let signature = hex::encode(signing_key.sign(message.as_bytes()).to_bytes()); |  | ||||||
| 
 |  | ||||||
|     Ok(signature) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn add_node(ip: String, info: NodeInfo) { |  | ||||||
|     let mut nodes = NODES.lock().unwrap(); |  | ||||||
|     nodes.insert(ip, info); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn remove_node(ip: &str) { |  | ||||||
|     let mut nodes = NODES.lock().unwrap(); |  | ||||||
|     nodes.remove(ip); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn get_pubkey(ip: &str) -> Option<NodeInfo> { |  | ||||||
|     let nodes = NODES.lock().unwrap(); |  | ||||||
|     nodes.get(ip).cloned() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn get_nodes_as_html_tabe() -> String { |  | ||||||
|     #[derive(Tabled)] |  | ||||||
|     struct OutputRow { |  | ||||||
|         ip: String, |  | ||||||
|         pubkey: String, |  | ||||||
|         age: u64, |  | ||||||
|     } |  | ||||||
|     let mut output = vec![]; |  | ||||||
|     for (ip, node_info) in NODES.lock().unwrap().iter() { |  | ||||||
|         let ip = ip.clone(); |  | ||||||
|         let pubkey = hex::encode(node_info.pubkey.as_bytes()); |  | ||||||
|         let age = std::time::SystemTime::now() |  | ||||||
|             .duration_since(node_info.updated_at) |  | ||||||
|             .unwrap_or(Duration::ZERO) |  | ||||||
|             .as_secs(); |  | ||||||
|         output.push(OutputRow { ip, pubkey, age }); |  | ||||||
|     } |  | ||||||
|     Table::new(output).to_string() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn cycle_keys() { |  | ||||||
|     thread::spawn(|| { |  | ||||||
|         let mut csprng = OsRng; |  | ||||||
|         loop { |  | ||||||
|             // TODO: save old private key to disk using SGX Sealing
 |  | ||||||
|             let privkey = ed25519_dalek::SigningKey::generate(&mut csprng); |  | ||||||
|             add_node( |  | ||||||
|                 "localhost".to_string(), |  | ||||||
|                 NodeInfo { |  | ||||||
|                     pubkey: privkey.verifying_key(), |  | ||||||
|                     updated_at: std::time::SystemTime::now(), |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             add_key(privkey.verifying_key(), privkey); |  | ||||||
|             thread::sleep(Duration::from_secs(60)); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
							
								
								
									
										218
									
								
								src/datastore.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										218
									
								
								src/datastore.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,218 @@ | |||||||
|  | #![allow(dead_code)] | ||||||
|  | use ed25519_dalek::{Signer, SigningKey, VerifyingKey}; | ||||||
|  | use once_cell::sync::Lazy; | ||||||
|  | use rand::rngs::OsRng; | ||||||
|  | use std::collections::HashMap; | ||||||
|  | use std::time::Duration; | ||||||
|  | use std::time::SystemTime; | ||||||
|  | use tabled::{Table, Tabled}; | ||||||
|  | use tokio::sync::Mutex; | ||||||
|  | 
 | ||||||
|  | type IP = String; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct NodeInfo { | ||||||
|  |     pub pubkey: VerifyingKey, | ||||||
|  |     pub updated_at: SystemTime, | ||||||
|  |     pub online: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Needs to be surrounded in an Arc.
 | ||||||
|  | pub struct Store { | ||||||
|  |     nodes: Mutex<HashMap<IP, NodeInfo>>, | ||||||
|  |     keys: Mutex<HashMap<VerifyingKey, SigningKey>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Store { | ||||||
|  |     pub fn init() -> Self { | ||||||
|  |         Self { | ||||||
|  |             nodes: Mutex::new(HashMap::new()), | ||||||
|  |             keys: Mutex::new(HashMap::new()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn add_mock_node(&self, ip: String) { | ||||||
|  |         let mut csprng = OsRng; | ||||||
|  |         let privkey = ed25519_dalek::SigningKey::generate(&mut csprng); | ||||||
|  |         { | ||||||
|  |             let mut nodes = self.nodes.lock().await; | ||||||
|  |             nodes.insert( | ||||||
|  |                 ip, | ||||||
|  |                 NodeInfo { | ||||||
|  |                     pubkey: privkey.verifying_key(), | ||||||
|  |                     updated_at: std::time::SystemTime::now(), | ||||||
|  |                     online: true, | ||||||
|  |                 }, | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         { | ||||||
|  |             let mut keys = self.keys.lock().await; | ||||||
|  |             keys.insert(privkey.verifying_key(), privkey); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn tabled_node_list(&self) -> String { | ||||||
|  |         #[derive(Tabled)] | ||||||
|  |         struct OutputRow { | ||||||
|  |             ip: String, | ||||||
|  |             pubkey: String, | ||||||
|  |             age: u64, | ||||||
|  |         } | ||||||
|  |         let mut output = vec![]; | ||||||
|  |         for (ip, node_info) in self.nodes.lock().await.iter() { | ||||||
|  |             let ip = ip.clone(); | ||||||
|  |             let pubkey = hex::encode(node_info.pubkey.as_bytes()); | ||||||
|  |             let age = std::time::SystemTime::now() | ||||||
|  |                 .duration_since(node_info.updated_at) | ||||||
|  |                 .unwrap_or(Duration::ZERO) | ||||||
|  |                 .as_secs(); | ||||||
|  |             output.push(OutputRow { ip, pubkey, age }); | ||||||
|  |         } | ||||||
|  |         Table::new(output).to_string() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn sign_message_with_key( | ||||||
|  |         &self, | ||||||
|  |         message: &str, | ||||||
|  |         pubkey: &str, | ||||||
|  |     ) -> Result<String, SigningError> { | ||||||
|  |         let key_bytes = hex::decode(pubkey)?; | ||||||
|  |         let pubkey = VerifyingKey::from_bytes(&key_bytes.as_slice().try_into()?)?; | ||||||
|  | 
 | ||||||
|  |         let key_store = self.keys.lock().await; | ||||||
|  |         let signing_key = match { key_store.get(&pubkey) } { | ||||||
|  |             Some(k) => k, | ||||||
|  |             None => return Err(SigningError::KeyNotFound), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // TODO: check if to_bytes returns the signature in a format that people can verify from bash
 | ||||||
|  |         let signature = hex::encode(signing_key.sign(message.as_bytes()).to_bytes()); | ||||||
|  | 
 | ||||||
|  |         Ok(signature) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static NODES: Lazy<Mutex<HashMap<String, NodeInfo>>> = Lazy::new(|| Mutex::new(HashMap::new())); | ||||||
|  | 
 | ||||||
|  | static KEYS: Lazy<Mutex<HashMap<VerifyingKey, SigningKey>>> = | ||||||
|  |     Lazy::new(|| Mutex::new(HashMap::new())); | ||||||
|  | 
 | ||||||
|  | pub enum SigningError { | ||||||
|  |     CorruptedKey, | ||||||
|  |     KeyNotFound, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl std::fmt::Display for SigningError { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         let error_message = match self { | ||||||
|  |             SigningError::CorruptedKey => "The public key is corrupted", | ||||||
|  |             SigningError::KeyNotFound => "Did not find the public key", | ||||||
|  |         }; | ||||||
|  |         write!(f, "{}", error_message) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<hex::FromHexError> for SigningError { | ||||||
|  |     fn from(_: hex::FromHexError) -> Self { | ||||||
|  |         Self::CorruptedKey | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<ed25519_dalek::ed25519::Error> for SigningError { | ||||||
|  |     fn from(_: ed25519_dalek::ed25519::Error) -> Self { | ||||||
|  |         Self::CorruptedKey | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<std::array::TryFromSliceError> for SigningError { | ||||||
|  |     fn from(_: std::array::TryFromSliceError) -> Self { | ||||||
|  |         Self::CorruptedKey | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub async fn add_key(pubkey: VerifyingKey, privkey: SigningKey) { | ||||||
|  |     let mut keys = KEYS.lock().await; | ||||||
|  |     keys.insert(pubkey, privkey); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub async fn remove_key(pubkey: &VerifyingKey) { | ||||||
|  |     let mut keys = KEYS.lock().await; | ||||||
|  |     keys.remove(pubkey); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async fn get_privkey(pubkey: &VerifyingKey) -> Option<SigningKey> { | ||||||
|  |     let keys = KEYS.lock().await; | ||||||
|  |     keys.get(pubkey).cloned() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub async fn sign_message_with_key(pubkey: &str, message: &str) -> Result<String, SigningError> { | ||||||
|  |     // Parse the hex string into a VerifyingKey
 | ||||||
|  |     let key_bytes = hex::decode(pubkey)?; | ||||||
|  |     let pubkey = VerifyingKey::from_bytes(&key_bytes.as_slice().try_into()?)?; | ||||||
|  | 
 | ||||||
|  |     // Lock the hashmap and try to get the SigningKey
 | ||||||
|  |     let key_store = KEYS.lock().await; | ||||||
|  |     let signing_key = match key_store.get(&pubkey) { | ||||||
|  |         Some(k) => k, | ||||||
|  |         None => return Err(SigningError::KeyNotFound), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // TODO: check if to_bytes returns the signature in a format that people can verify from bash
 | ||||||
|  |     let signature = hex::encode(signing_key.sign(message.as_bytes()).to_bytes()); | ||||||
|  | 
 | ||||||
|  |     Ok(signature) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub async fn add_node(ip: String, info: NodeInfo) { | ||||||
|  |     let mut nodes = NODES.lock().await; | ||||||
|  |     nodes.insert(ip, info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub async fn remove_node(ip: &str) { | ||||||
|  |     let mut nodes = NODES.lock().await; | ||||||
|  |     nodes.remove(ip); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub async fn get_pubkey(ip: &str) -> Option<NodeInfo> { | ||||||
|  |     let nodes = NODES.lock().await; | ||||||
|  |     nodes.get(ip).cloned() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub async fn get_nodes_as_html_tabe() -> String { | ||||||
|  |     #[derive(Tabled)] | ||||||
|  |     struct OutputRow { | ||||||
|  |         ip: String, | ||||||
|  |         pubkey: String, | ||||||
|  |         age: u64, | ||||||
|  |     } | ||||||
|  |     let mut output = vec![]; | ||||||
|  |     for (ip, node_info) in NODES.lock().await.iter() { | ||||||
|  |         let ip = ip.clone(); | ||||||
|  |         let pubkey = hex::encode(node_info.pubkey.as_bytes()); | ||||||
|  |         let age = std::time::SystemTime::now() | ||||||
|  |             .duration_since(node_info.updated_at) | ||||||
|  |             .unwrap_or(Duration::ZERO) | ||||||
|  |             .as_secs(); | ||||||
|  |         output.push(OutputRow { ip, pubkey, age }); | ||||||
|  |     } | ||||||
|  |     Table::new(output).to_string() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // pub fn cycle_keys() {
 | ||||||
|  | //     thread::spawn(|| {
 | ||||||
|  | //         let mut csprng = OsRng;
 | ||||||
|  | //         loop {
 | ||||||
|  | //             // TODO: save old private key to disk using SGX Sealing
 | ||||||
|  | //             let privkey = ed25519_dalek::SigningKey::generate(&mut csprng);
 | ||||||
|  | //             add_node(
 | ||||||
|  | //                 "localhost".to_string(),
 | ||||||
|  | //                 NodeInfo {
 | ||||||
|  | //                     pubkey: privkey.verifying_key(),
 | ||||||
|  | //                     updated_at: std::time::SystemTime::now(),
 | ||||||
|  | //                 },
 | ||||||
|  | //             );
 | ||||||
|  | //             add_key(privkey.verifying_key(), privkey);
 | ||||||
|  | //             thread::sleep(Duration::from_secs(60));
 | ||||||
|  | //         }
 | ||||||
|  | //     });
 | ||||||
|  | // }
 | ||||||
| @ -1,13 +1,18 @@ | |||||||
| use crate::database::get_nodes_as_html_tabe; | use crate::datastore::Store; | ||||||
|  | use std::sync::Arc; | ||||||
|  | 
 | ||||||
| use salvo::prelude::*; | use salvo::prelude::*; | ||||||
|  | use salvo::affix; | ||||||
| 
 | 
 | ||||||
| #[handler] | #[handler] | ||||||
| async fn homepage() -> String { | async fn homepage(depot: &mut Depot) -> String { | ||||||
|     get_nodes_as_html_tabe() |     let ds = depot.obtain::<Arc<Store>>().unwrap(); | ||||||
|  |     ds.tabled_node_list().await | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[handler] | #[handler] | ||||||
| async fn sign(req: &mut Request) -> String { | async fn sign(req: &mut Request, depot: &mut Depot) -> String { | ||||||
|  |     let ds = depot.obtain::<Arc<Store>>().unwrap(); | ||||||
|     let pubkey = match req.query::<String>("pubkey") { |     let pubkey = match req.query::<String>("pubkey") { | ||||||
|         Some(k) => k, |         Some(k) => k, | ||||||
|         None => return "pubkey must be specified as GET param".to_string(), |         None => return "pubkey must be specified as GET param".to_string(), | ||||||
| @ -18,17 +23,22 @@ async fn sign(req: &mut Request) -> String { | |||||||
|         None => return "something must be specified as GET param".to_string(), |         None => return "something must be specified as GET param".to_string(), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     match crate::database::sign_message_with_key(&pubkey, &something) { |     match ds.sign_message_with_key(&something, &pubkey).await { | ||||||
|         Ok(s) => s, |         Ok(s) => s, | ||||||
|         Err(e) => e.to_string(), |         Err(e) => e.to_string(), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn start() { | pub fn init(ds: Arc<Store>) -> std::thread::JoinHandle<()> { | ||||||
|  |     std::thread::spawn(|| { | ||||||
|  |         tokio::runtime::Runtime::new().unwrap().block_on(async { | ||||||
|             let acceptor = TcpListener::new("0.0.0.0:31372").bind().await; |             let acceptor = TcpListener::new("0.0.0.0:31372").bind().await; | ||||||
|             let router = Router::new() |             let router = Router::new() | ||||||
|  |                 .hoop(affix::inject(ds)) | ||||||
|                 .get(homepage) |                 .get(homepage) | ||||||
|                 .push(Router::with_path("sign").get(sign)); |                 .push(Router::with_path("sign").get(sign)); | ||||||
|             println!("{:?}", router); |             println!("{:?}", router); | ||||||
|             Server::new(acceptor).serve(router).await; |             Server::new(acceptor).serve(router).await; | ||||||
|  |         }); | ||||||
|  |     }) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								src/main.rs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										16
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,12 +1,14 @@ | |||||||
| mod grpc; | mod datastore; | ||||||
| mod http_server; | mod http_server; | ||||||
| mod database; | use crate::datastore::Store; | ||||||
|  | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| #[tokio::main] | #[tokio::main] | ||||||
| async fn main() { | async fn main() { | ||||||
|     crate::database::cycle_keys(); |     let ds: Arc<Store> = Arc::new(Store::init()); | ||||||
|     grpc::add_node("1.1.1.1".to_string()); |     ds.add_mock_node("1.1.1.1".to_string()).await; | ||||||
|     grpc::add_node("1.2.3.4".to_string()); |     ds.add_mock_node("1.2.3.4".to_string()).await; | ||||||
|     grpc::add_node("2.2.2.2".to_string()); |     ds.add_mock_node("1.2.2.2".to_string()).await; | ||||||
|     crate::http_server::start().await; |     let thread_result = crate::http_server::init(ds).join(); | ||||||
|  |     println!("{thread_result:?}"); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user