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<()> {
|
||||||
let acceptor = TcpListener::new("0.0.0.0:31372").bind().await;
|
std::thread::spawn(|| {
|
||||||
let router = Router::new()
|
tokio::runtime::Runtime::new().unwrap().block_on(async {
|
||||||
.get(homepage)
|
let acceptor = TcpListener::new("0.0.0.0:31372").bind().await;
|
||||||
.push(Router::with_path("sign").get(sign));
|
let router = Router::new()
|
||||||
println!("{:?}", router);
|
.hoop(affix::inject(ds))
|
||||||
Server::new(acceptor).serve(router).await;
|
.get(homepage)
|
||||||
|
.push(Router::with_path("sign").get(sign));
|
||||||
|
println!("{:?}", router);
|
||||||
|
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