Add message signing
All messages are now signed by default with a randomly generated key. TODO still is keyserver stuff, locking down the config file potentially (or telling users to do it in the readme?), and doing any sort of verification
This commit is contained in:
+9
-1
@@ -1,4 +1,5 @@
|
||||
use clap::{Parser,Subcommand,ArgAction};
|
||||
use ed25519_dalek::SigningKey;
|
||||
use fedichat::client::{ClientMessage,SignedClientMessage,AuthMethod};
|
||||
use fedichat::ServerAddr;
|
||||
use fedichat::state::StatePath;
|
||||
@@ -181,7 +182,7 @@ impl Command {
|
||||
}
|
||||
// If a command needs multiple messages, like an auth message first
|
||||
// then call this
|
||||
pub fn generate_messages(self,username: String,token: Option<String>)
|
||||
pub fn generate_messages(self,username: String,token: Option<String>, key: SigningKey)
|
||||
-> Result<Vec<SignedClientMessage>,MessageError>
|
||||
{
|
||||
let mut messages = Vec::with_capacity(2);
|
||||
@@ -211,6 +212,11 @@ impl Command {
|
||||
signature: Box::new([0])});
|
||||
}
|
||||
|
||||
// sign all the messages
|
||||
for message in messages.iter_mut() {
|
||||
message.sign(key.clone())?;
|
||||
}
|
||||
|
||||
Ok(messages)
|
||||
}
|
||||
}
|
||||
@@ -223,6 +229,8 @@ pub enum MessageError {
|
||||
UuidError(#[from] uuid::Error),
|
||||
#[error("Error during file IO: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("Error while processing signature: {0}")]
|
||||
Signature(#[from] fedichat::client::SignatureError)
|
||||
|
||||
}
|
||||
|
||||
|
||||
+41
-5
@@ -1,4 +1,6 @@
|
||||
use expanduser::expanduser;
|
||||
use rand::rngs::OsRng;
|
||||
use ed25519_dalek::SigningKey;
|
||||
use serde::{Serialize,Deserialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
@@ -62,14 +64,41 @@ impl Config {
|
||||
|
||||
pub fn insert_token(&mut self,server: &String,username: String, token: String) -> Result<(),ConfigError> {
|
||||
let _ = self.servers.get_mut(server).ok_or(ConfigError::ServerNotFound(server.clone()))?
|
||||
.users.insert(username,token);
|
||||
.users.get_mut(&username).ok_or(ConfigError::UserNotFound(username.clone()))?.token = Some(token);
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
pub fn get_token(&self,server: &String, username: &String) -> Result<Option<String>,ConfigError> {
|
||||
// TODO: Dont really have to clone the return value probably. Should refactor at some point
|
||||
Ok(self.servers.get(server).ok_or(ConfigError::ServerNotFound(server.clone()))?
|
||||
.users.get(username).cloned())
|
||||
.users.get(username).ok_or(ConfigError::UserNotFound(username.clone()))?.token.clone())
|
||||
}
|
||||
|
||||
// Set up key if user does not exist
|
||||
//
|
||||
// If user does exist they are guaranteed to already have a key
|
||||
//
|
||||
// Maybe at some point there should be a command to load/save/regenerate keys
|
||||
pub fn maybe_gen_key(&mut self, server: &String, username: &String) -> Result<(),ConfigError>{
|
||||
if !self.servers.contains_key(server) {
|
||||
self.servers.insert(server.to_string(),Server{users: HashMap::new()});
|
||||
}
|
||||
let server = self.servers.get_mut(server).ok_or(ConfigError::ServerNotFound(server.clone()))?;
|
||||
|
||||
if !server.users.contains_key(username) {
|
||||
let mut rng = OsRng;
|
||||
let key = SigningKey::generate(&mut rng);
|
||||
server.users.insert(username.to_string(),User{token: None, key});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_key(&self,server: &String, username: &String) -> Result<SigningKey,ConfigError>{
|
||||
|
||||
Ok(self.servers.get(server).ok_or(ConfigError::ServerNotFound(server.clone()))?
|
||||
.users.get(username).ok_or(ConfigError::UserNotFound(username.clone()))?.key.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +107,14 @@ impl Config {
|
||||
#[derive(Serialize,Deserialize)]
|
||||
pub struct Server {
|
||||
// user to token mapping
|
||||
users: HashMap<String,String>
|
||||
users: HashMap<String,User>
|
||||
}
|
||||
|
||||
// NOTE: Storing keys in the config file means we need to lock down read access
|
||||
#[derive(Serialize,Deserialize,Clone)]
|
||||
pub struct User {
|
||||
pub token: Option<String>,
|
||||
pub key: ed25519_dalek::SigningKey
|
||||
}
|
||||
|
||||
#[derive(Error,Debug)]
|
||||
@@ -91,8 +127,8 @@ pub enum ConfigError {
|
||||
#[error("Error while serializing config file: {0}")]
|
||||
SerError(#[from] toml::ser::Error),
|
||||
|
||||
//#[error("User `{0}` not found in config")]
|
||||
//UserNotFound(String),
|
||||
#[error("User `{0}` not found in config")]
|
||||
UserNotFound(String),
|
||||
#[error("Server `{0}` not found in config")]
|
||||
ServerNotFound(String),
|
||||
|
||||
|
||||
+6
-1
@@ -57,10 +57,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err(e) => return Err(Box::new(e).into())
|
||||
};
|
||||
|
||||
// set homeserver
|
||||
let target_server = match cli.server {
|
||||
Some(s) => s,
|
||||
None => config.default_server.clone()
|
||||
};
|
||||
// Generate a key for the specified user if they do not already exist
|
||||
config.maybe_gen_key(&target_server,&cli.username)?;
|
||||
let token = config.get_token(&target_server,&cli.username)?;
|
||||
|
||||
debug!("Target server is {target_server}");
|
||||
@@ -83,9 +86,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Command::Fetch{file, ..} = &cli.command {
|
||||
file_to_write = Some(file.clone());
|
||||
}
|
||||
|
||||
let key = config.get_key(&target_server,&cli.username)?;
|
||||
|
||||
// send messages, each time waiting for a response
|
||||
let messages = cli.command.generate_messages(cli.username.clone(),token)?;
|
||||
let messages = cli.command.generate_messages(cli.username.clone(),token,key)?;
|
||||
|
||||
debug!("Sending commands");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user