Skip to content

Commit

Permalink
Initializes KeyMgr
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Apr 22, 2024
1 parent fffb9c3 commit d3c0623
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 13 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ version = "0.1.0"
edition = "2021"

[dependencies]
aes = "0.8.4"
clap = "4.4"
dirs = "5.0.1"
erdp = "0.1.0"
getrandom = { version = "0.2.14", features = ["std"] }
serde = { version = "1.0.197", features = ["derive"] }
serde_yaml = "0.9.34"
sha3 = "0.10.8"
tabled = "0.15.0"
thiserror = "1.0.58"
ureq = "2.9.6"
url = { version = "2.5.0", features = ["serde"] }
zeroize = "1.7.0"
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ computers easily.
## Features

- Cross-platform.
- Open-sourced, both client and server.
- Open-source, the client are licensed under GPL and the server are licensed under AGPL.
- Self-hosting supports.
- Secure and private. All informations stored on the server are encrypted by your keys. These keys never leave your computer.

## License

Expand Down
13 changes: 11 additions & 2 deletions src/cmd/init.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::Key;
use crate::config::AppConfig;
use crate::key::KeyMgr;
use clap::{value_parser, Arg, ArgMatches, Command};
use std::path::PathBuf;
use std::process::ExitCode;
Expand All @@ -7,13 +9,14 @@ use std::sync::Arc;
/// Command to initialize a new respotiroy.
pub struct Init {
config: Arc<AppConfig>,
keymgr: Arc<KeyMgr>,
}

impl Init {
pub const NAME: &'static str = "init";

pub fn new(config: Arc<AppConfig>) -> Self {
Self { config }
pub fn new(config: Arc<AppConfig>, keymgr: Arc<KeyMgr>) -> Self {
Self { config, keymgr }
}
}

Expand Down Expand Up @@ -49,6 +52,12 @@ impl super::Command for Init {
}

fn exec(&self, _: &ArgMatches) -> ExitCode {
// Check if we have at least one key to encrypt.
if self.keymgr.keys().len() == 0 {
eprintln!("No file encryption keys available, invoke Warp with '{} --help' to see how to create a new key.", Key::NAME);
return ExitCode::FAILURE;
}

todo!()
}
}
30 changes: 30 additions & 0 deletions src/cmd/key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use clap::{ArgMatches, Command};
use std::process::ExitCode;

/// Command to manage file encryption keys.
pub struct Key {}

impl Key {
pub const NAME: &'static str = "key";

pub fn new() -> Self {
Self {}
}
}

impl super::Command for Key {
fn is_matched(&self, name: &str) -> bool {
name == Self::NAME
}

fn definition(&self) -> Command {
Command::new(Self::NAME)
.about("Manage file encryption keys")
.subcommand_required(true)
.subcommand(Command::new("ls").about("List all available keys"))
}

fn exec(&self, _: &ArgMatches) -> ExitCode {
todo!()
}
}
51 changes: 51 additions & 0 deletions src/cmd/keystore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::key::KeyMgr;
use clap::{ArgMatches, Command};
use std::process::ExitCode;
use std::sync::Arc;

/// Command to manage file encryption keystores.
pub struct Keystore {
keymgr: Arc<KeyMgr>,
}

impl Keystore {
pub const NAME: &'static str = "keystore";

pub fn new(keymgr: Arc<KeyMgr>) -> Self {
Self { keymgr }
}

fn ls(&self) -> ExitCode {
let mut t = tabled::builder::Builder::new();

t.push_record(["ID"]);

for s in self.keymgr.stores() {
t.push_record([s.id()]);
}

println!("{}", t.build());

ExitCode::SUCCESS
}
}

impl super::Command for Keystore {
fn is_matched(&self, name: &str) -> bool {
name == Self::NAME
}

fn definition(&self) -> Command {
Command::new(Self::NAME)
.about("Manage file encryption keystores")
.subcommand_required(true)
.subcommand(Command::new("ls").about("List all enabled keystores"))
}

fn exec(&self, args: &ArgMatches) -> ExitCode {
match args.subcommand().unwrap() {
("ls", _) => self.ls(),
_ => unreachable!(),
}
}
}
4 changes: 4 additions & 0 deletions src/cmd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
pub use self::init::*;
pub use self::key::*;
pub use self::keystore::*;
use std::process::ExitCode;

mod init;
mod key;
mod keystore;

/// A single command passed from a command line argument.
pub trait Command {
Expand Down
23 changes: 17 additions & 6 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,32 @@ use url::Url;

/// Application configurations.
#[derive(Deserialize)]
#[serde(default)]
pub struct AppConfig {
#[serde(default = "AppConfig::default_default_server")]
pub default_server: Url,
pub key: Key,
}

impl AppConfig {
fn default_default_server() -> Url {
Url::parse("https://api.warpgate.sh").unwrap()
impl Default for AppConfig {
fn default() -> Self {
Self {
default_server: Url::parse("https://api.warpgate.sh").unwrap(),
key: Key::default(),
}
}
}

impl Default for AppConfig {
/// Configurations for encryption key.
#[derive(Deserialize)]
#[serde(default)]
pub struct Key {
pub default_storage: bool,
}

impl Default for Key {
fn default() -> Self {
Self {
default_server: Self::default_default_server(),
default_storage: true,
}
}
}
62 changes: 62 additions & 0 deletions src/key/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use self::store::{DefaultStore, Keystore};
use crate::config::AppConfig;
use crate::home::Home;
use std::collections::HashMap;
use std::iter::FusedIterator;
use std::sync::Arc;
use thiserror::Error;

mod store;

/// Manage file encryption keys.
pub struct KeyMgr {
stores: HashMap<&'static str, Arc<dyn Keystore>>,
keys: HashMap<KeyId, Key>,
}

impl KeyMgr {
pub fn new(home: &Arc<Home>, config: &Arc<AppConfig>) -> Result<Self, KeyMgrError> {
let mut stores = HashMap::<&'static str, Arc<dyn Keystore>>::new();
let mut keys = HashMap::new();

// Initialize default store.
if config.key.default_storage {
let s = Arc::new(DefaultStore::new(home));

for k in s.list() {
assert!(keys.insert(k.id().clone(), k).is_none());
}

assert!(stores.insert(s.id(), s).is_none());
}

Ok(Self { stores, keys })
}

pub fn stores(&self) -> impl Iterator<Item = &dyn Keystore> + FusedIterator {
self.stores.values().map(|s| s.as_ref())
}

pub fn keys(&self) -> impl Iterator<Item = &Key> + ExactSizeIterator + FusedIterator {
self.keys.values()
}
}

/// Unique identifier of a [`Key`].
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct KeyId([u8; 16]);

/// Key to encrypt/decrypt files in a repository.
pub struct Key {
id: KeyId,
}

impl Key {
pub fn id(&self) -> &KeyId {
&self.id
}
}

/// Represents an error when [`KeyMgr`] fails to initialize.
#[derive(Debug, Error)]
pub enum KeyMgrError {}
76 changes: 76 additions & 0 deletions src/key/store/default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use super::Keystore;
use crate::home::Home;
use crate::key::Key;
use aes::cipher::{BlockEncrypt, KeyInit};
use aes::Aes128;
use getrandom::getrandom;
use sha3::digest::{ExtendableOutput, Update, XofReader};
use sha3::Shake128;
use std::error::Error;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use thiserror::Error;
use zeroize::Zeroizing;

/// Implementation of [`Keystore`] using native key store of the OS.
pub struct DefaultStore {}

impl DefaultStore {
pub fn new(_: &Home) -> Self {
Self {}
}
}

impl Keystore for DefaultStore {
fn id(&self) -> &'static str {
"default"
}

fn list(self: &Arc<Self>) -> impl Iterator<Item = Key>
where
Self: Sized,
{
KeyList {}
}

fn new(self: Arc<Self>) -> Result<Key, Box<dyn Error>> {
// Generate a new key.
let mut key = Zeroizing::new([0u8; 16]);

if let Err(e) = getrandom(key.deref_mut()) {
return Err(Box::new(NewError::GenerateKeyFailed(e)));
}

// Get a key check value.
let mut kcv = [0u8; 16];

Aes128::new(key.deref().into()).encrypt_block((&mut kcv).into());

// Get key ID.
let mut hasher = Shake128::default();
let mut id = [0u8; 16];

hasher.update(&kcv);
hasher.finalize_xof().read(&mut id);

todo!()
}
}

/// Iterator to list all keys in the [`DefaultStore`].
struct KeyList {}

impl Iterator for KeyList {
type Item = Key;

fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}

/// Represents an error when [`DefaultStore::new()`] fails.
#[derive(Debug, Error)]
enum NewError {
#[error("couldn't generate a new key")]
GenerateKeyFailed(#[source] getrandom::Error),
}
17 changes: 17 additions & 0 deletions src/key/store/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pub use self::default::*;
use super::Key;
use std::error::Error;
use std::sync::Arc;

mod default;

/// Storage to keep encryption keys.
pub trait Keystore {
fn id(&self) -> &'static str;

fn list(self: &Arc<Self>) -> impl Iterator<Item = Key>
where
Self: Sized;

fn new(self: Arc<Self>) -> Result<Key, Box<dyn Error>>;
}
Loading

0 comments on commit d3c0623

Please sign in to comment.