aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-05-19 14:33:49 +0200
committerAlex Auvolat <alex@adnab.me>2022-05-19 14:33:49 +0200
commit6be90936a108d971e0cfa3ddaa9c2d54557e30f3 (patch)
treeed0dae1b4ebf3215b808b81d9980376e9b5dee26
parent1ac56a91981bee4867dfb054bd2199c6111fe1eb (diff)
downloadaerogramme-6be90936a108d971e0cfa3ddaa9c2d54557e30f3.tar.gz
aerogramme-6be90936a108d971e0cfa3ddaa9c2d54557e30f3.zip
Some refactoring
-rw-r--r--src/bayou.rs10
-rw-r--r--src/config.rs3
-rw-r--r--src/cryptoblob.rs13
-rw-r--r--src/login/ldap_provider.rs3
-rw-r--r--src/login/mod.rs76
-rw-r--r--src/login/static_provider.rs37
-rw-r--r--src/mailbox.rs12
-rw-r--r--src/main.rs52
-rw-r--r--src/server.rs49
9 files changed, 173 insertions, 82 deletions
diff --git a/src/bayou.rs b/src/bayou.rs
index 97281cd..acca1e5 100644
--- a/src/bayou.rs
+++ b/src/bayou.rs
@@ -57,18 +57,16 @@ pub struct Bayou<S: BayouState> {
impl<S: BayouState> Bayou<S> {
pub fn new(
- k2v_region: &Region,
- s3_region: &Region,
creds: &Credentials,
path: String,
) -> Result<Self> {
- let k2v_client = creds.k2v_client(k2v_region)?;
- let s3_client = creds.s3_client(s3_region)?;
+ let k2v_client = creds.k2v_client()?;
+ let s3_client = creds.s3_client()?;
Ok(Self {
- bucket: creds.bucket.clone(),
+ bucket: creds.bucket().to_string(),
path,
- key: creds.master_key.clone(),
+ key: creds.keys.master.clone(),
k2v: k2v_client,
s3: s3_client,
checkpoint: (Timestamp::zero(), S::default()),
diff --git a/src/config.rs b/src/config.rs
index 47db90d..d756d6e 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -25,10 +25,13 @@ pub struct LoginStaticConfig {
#[derive(Deserialize, Debug, Clone)]
pub struct LoginStaticUser {
pub password: String,
+
pub aws_access_key_id: String,
pub aws_secret_access_key: String,
pub bucket: Option<String>,
+
pub master_key: Option<String>,
+ pub secret_key: Option<String>,
}
#[derive(Deserialize, Debug, Clone)]
diff --git a/src/cryptoblob.rs b/src/cryptoblob.rs
index 6e51dbb..ad05521 100644
--- a/src/cryptoblob.rs
+++ b/src/cryptoblob.rs
@@ -5,17 +5,22 @@ use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use zstd::stream::{decode_all as zstd_decode, encode_all as zstd_encode};
-use sodiumoxide::crypto::secretbox::xsalsa20poly1305::{self, gen_nonce, Nonce, NONCEBYTES};
+use sodiumoxide::crypto::secretbox::xsalsa20poly1305 as secretbox;
+use sodiumoxide::crypto::box_ as publicbox;
+
pub use sodiumoxide::crypto::secretbox::xsalsa20poly1305::{gen_key, Key, KEYBYTES};
+pub use sodiumoxide::crypto::box_::{gen_keypair, PublicKey, SecretKey, PUBLICKEYBYTES, SECRETKEYBYTES};
pub fn open(cryptoblob: &[u8], key: &Key) -> Result<Vec<u8>> {
+ use secretbox::{NONCEBYTES, Nonce};
+
if cryptoblob.len() < NONCEBYTES {
return Err(anyhow!("Cyphertext too short"));
}
// Decrypt -> get Zstd data
let nonce = Nonce::from_slice(&cryptoblob[..NONCEBYTES]).unwrap();
- let zstdblob = xsalsa20poly1305::open(&cryptoblob[NONCEBYTES..], &nonce, key)
+ let zstdblob = secretbox::open(&cryptoblob[NONCEBYTES..], &nonce, key)
.map_err(|_| anyhow!("Could not decrypt blob"))?;
// Decompress zstd data
@@ -26,13 +31,15 @@ pub fn open(cryptoblob: &[u8], key: &Key) -> Result<Vec<u8>> {
}
pub fn seal(plainblob: &[u8], key: &Key) -> Result<Vec<u8>> {
+ use secretbox::{NONCEBYTES, gen_nonce};
+
// Compress data using zstd
let mut reader = &plainblob[..];
let zstdblob = zstd_encode(&mut reader, 0)?;
// Encrypt
let nonce = gen_nonce();
- let cryptoblob = xsalsa20poly1305::seal(&zstdblob, &nonce, key);
+ let cryptoblob = secretbox::seal(&zstdblob, &nonce, key);
let mut res = Vec::with_capacity(NONCEBYTES + cryptoblob.len());
res.extend(nonce.as_ref());
diff --git a/src/login/ldap_provider.rs b/src/login/ldap_provider.rs
index 6108e6e..54ddbd5 100644
--- a/src/login/ldap_provider.rs
+++ b/src/login/ldap_provider.rs
@@ -1,5 +1,6 @@
use anyhow::Result;
use async_trait::async_trait;
+use rusoto_signature::Region;
use crate::config::*;
use crate::login::*;
@@ -9,7 +10,7 @@ pub struct LdapLoginProvider {
}
impl LdapLoginProvider {
- pub fn new(_config: LoginLdapConfig) -> Result<Self> {
+ pub fn new(_config: LoginLdapConfig, _k2v_region: Region, _s3_region: Region) -> Result<Self> {
unimplemented!()
}
}
diff --git a/src/login/mod.rs b/src/login/mod.rs
index 0845371..4022962 100644
--- a/src/login/mod.rs
+++ b/src/login/mod.rs
@@ -9,7 +9,7 @@ use rusoto_credential::{AwsCredentials, StaticProvider};
use rusoto_s3::S3Client;
use rusoto_signature::Region;
-use crate::cryptoblob::Key as SymmetricKey;
+use crate::cryptoblob::*;
#[async_trait]
pub trait LoginProvider {
@@ -18,14 +18,51 @@ pub trait LoginProvider {
#[derive(Clone, Debug)]
pub struct Credentials {
+ pub storage: StorageCredentials,
+ pub keys: CryptoKeys,
+}
+
+#[derive(Clone, Debug)]
+pub struct StorageCredentials {
+ pub s3_region: Region,
+ pub k2v_region: Region,
+
pub aws_access_key_id: String,
pub aws_secret_access_key: String,
pub bucket: String,
- pub master_key: SymmetricKey,
}
+#[derive(Clone, Debug)]
+pub struct CryptoKeys {
+ // Master key for symmetric encryption of mailbox data
+ pub master: Key,
+ // Public/private keypair for encryption of incomming emails
+ pub secret: SecretKey,
+ pub public: PublicKey,
+}
+
+// ----
+
impl Credentials {
- pub fn k2v_client(&self, k2v_region: &Region) -> Result<K2vClient> {
+ pub fn k2v_client(&self) -> Result<K2vClient> {
+ self.storage.k2v_client()
+ }
+ pub fn s3_client(&self) -> Result<S3Client> {
+ self.storage.s3_client()
+ }
+ pub fn bucket(&self) -> &str {
+ self.storage.bucket.as_str()
+ }
+ pub fn dump_config(&self) {
+ println!("aws_access_key_id = \"{}\"", self.storage.aws_access_key_id);
+ println!("aws_secret_access_key = \"{}\"", self.storage.aws_secret_access_key);
+ println!("master_key = \"{}\"", base64::encode(&self.keys.master));
+ println!("secret_key = \"{}\"", base64::encode(&self.keys.secret));
+ }
+}
+
+impl StorageCredentials {
+ pub fn k2v_client(&self) -> Result<K2vClient> {
let aws_creds = AwsCredentials::new(
self.aws_access_key_id.clone(),
self.aws_secret_access_key.clone(),
@@ -34,14 +71,14 @@ impl Credentials {
);
Ok(K2vClient::new(
- k2v_region.clone(),
+ self.k2v_region.clone(),
self.bucket.clone(),
aws_creds,
None,
)?)
}
- pub fn s3_client(&self, s3_region: &Region) -> Result<S3Client> {
+ pub fn s3_client(&self) -> Result<S3Client> {
let aws_creds_provider = StaticProvider::new_minimal(
self.aws_access_key_id.clone(),
self.aws_secret_access_key.clone(),
@@ -50,7 +87,34 @@ impl Credentials {
Ok(S3Client::new_with(
HttpClient::new()?,
aws_creds_provider,
- s3_region.clone(),
+ self.s3_region.clone(),
))
}
}
+
+impl CryptoKeys {
+ pub fn init(storage: &StorageCredentials) -> Result<Self> {
+ unimplemented!()
+ }
+
+ pub fn init_without_password(storage: &StorageCredentials, master_key: &Key, secret_key: &SecretKey) -> Result<Self> {
+ unimplemented!()
+ }
+
+ pub fn open(storage: &StorageCredentials, password: &str) -> Result<Self> {
+ unimplemented!()
+ }
+
+ pub fn open_without_password(storage: &StorageCredentials, master_key: &Key, secret_key: &SecretKey) -> Result<Self> {
+ unimplemented!()
+ }
+
+ pub fn add_password(&self, storage: &StorageCredentials, password: &str) -> Result<()> {
+ unimplemented!()
+ }
+
+ pub fn remove_password(&self, storage: &StorageCredentials, password: &str, allow_remove_all: bool) -> Result<()> {
+ unimplemented!()
+ }
+}
+
diff --git a/src/login/static_provider.rs b/src/login/static_provider.rs
index 037948a..d7d791a 100644
--- a/src/login/static_provider.rs
+++ b/src/login/static_provider.rs
@@ -5,21 +5,23 @@ use async_trait::async_trait;
use rusoto_signature::Region;
use crate::config::*;
-use crate::cryptoblob::Key;
+use crate::cryptoblob::{Key, SecretKey};
use crate::login::*;
pub struct StaticLoginProvider {
default_bucket: Option<String>,
users: HashMap<String, LoginStaticUser>,
k2v_region: Region,
+ s3_region: Region,
}
impl StaticLoginProvider {
- pub fn new(config: LoginStaticConfig, k2v_region: Region) -> Result<Self> {
+ pub fn new(config: LoginStaticConfig, k2v_region: Region, s3_region: Region) -> Result<Self> {
Ok(Self {
default_bucket: config.default_bucket,
users: config.users,
k2v_region,
+ s3_region,
})
}
}
@@ -42,18 +44,31 @@ impl LoginProvider for StaticLoginProvider {
"No bucket configured and no default bucket specieid"
))?;
- // TODO if master key is not specified, retrieve it from K2V key storage
- let master_key_str = u.master_key.as_ref().ok_or(anyhow!(
- "Master key must be specified in config file for now, this will change"
- ))?;
- let master_key = Key::from_slice(&base64::decode(master_key_str)?)
- .ok_or(anyhow!("Invalid master key"))?;
-
- Ok(Credentials {
+ let storage = StorageCredentials {
+ k2v_region: self.k2v_region.clone(),
+ s3_region: self.s3_region.clone(),
aws_access_key_id: u.aws_access_key_id.clone(),
aws_secret_access_key: u.aws_secret_access_key.clone(),
bucket,
- master_key,
+ };
+
+ let keys = match (&u.master_key, &u.secret_key) {
+ (Some(m), Some(s)) => {
+ let master_key = Key::from_slice(&base64::decode(m)?)
+ .ok_or(anyhow!("Invalid master key"))?;
+ let secret_key = SecretKey::from_slice(&base64::decode(m)?)
+ .ok_or(anyhow!("Invalid secret key"))?;
+ CryptoKeys::open_without_password(&storage, &master_key, &secret_key)?
+ }
+ (None, None) => {
+ CryptoKeys::open(&storage, password)?
+ }
+ _ => bail!("Either both master and secret key or none of them must be specified for user"),
+ };
+
+ Ok(Credentials {
+ storage,
+ keys,
})
}
}
diff --git a/src/mailbox.rs b/src/mailbox.rs
index d997691..44b9f95 100644
--- a/src/mailbox.rs
+++ b/src/mailbox.rs
@@ -22,19 +22,17 @@ pub struct Mailbox {
impl Mailbox {
pub async fn new(
- k2v_region: &Region,
- s3_region: &Region,
creds: &Credentials,
name: String,
) -> Result<Self> {
- let uid_index = Bayou::<UidIndex>::new(k2v_region, s3_region, creds, name.clone())?;
+ let uid_index = Bayou::<UidIndex>::new(creds, name.clone())?;
Ok(Self {
- bucket: creds.bucket.clone(),
+ bucket: creds.bucket().to_string(),
name,
- key: creds.master_key.clone(),
- k2v: creds.k2v_client(&k2v_region)?,
- s3: creds.s3_client(&s3_region)?,
+ key: creds.keys.master.clone(),
+ k2v: creds.k2v_client()?,
+ s3: creds.s3_client()?,
uid_index,
})
}
diff --git a/src/main.rs b/src/main.rs
index 40dfbb8..ca66137 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,6 +5,7 @@ mod login;
mod mailbox;
mod time;
mod uidindex;
+mod server;
use anyhow::{bail, Result};
use std::sync::Arc;
@@ -14,6 +15,7 @@ use rusoto_signature::Region;
use config::*;
use login::{ldap_provider::*, static_provider::*, *};
use mailbox::Mailbox;
+use server::Server;
#[tokio::main]
async fn main() {
@@ -26,53 +28,7 @@ async fn main() {
async fn main2() -> Result<()> {
let config = read_config("mailrage.toml".into())?;
- let main = Main::new(config)?;
- main.run().await
+ let server = Server::new(config)?;
+ server.run().await
}
-struct Main {
- pub s3_region: Region,
- pub k2v_region: Region,
- pub login_provider: Box<dyn LoginProvider>,
-}
-
-impl Main {
- fn new(config: Config) -> Result<Arc<Self>> {
- let s3_region = Region::Custom {
- name: config.s3_region,
- endpoint: config.s3_endpoint,
- };
- let k2v_region = Region::Custom {
- name: config.k2v_region,
- endpoint: config.k2v_endpoint,
- };
- let login_provider: Box<dyn LoginProvider> = match (config.login_static, config.login_ldap)
- {
- (Some(st), None) => Box::new(StaticLoginProvider::new(st, k2v_region.clone())?),
- (None, Some(ld)) => Box::new(LdapLoginProvider::new(ld)?),
- (Some(_), Some(_)) => bail!("A single login provider must be set up in config file"),
- (None, None) => bail!("No login provider is set up in config file"),
- };
- Ok(Arc::new(Self {
- s3_region,
- k2v_region,
- login_provider,
- }))
- }
-
- async fn run(self: &Arc<Self>) -> Result<()> {
- let creds = self.login_provider.login("lx", "plop").await?;
-
- let mut mailbox = Mailbox::new(
- &self.k2v_region,
- &self.s3_region,
- &creds,
- "TestMailbox".to_string(),
- )
- .await?;
-
- mailbox.test().await?;
-
- Ok(())
- }
-}
diff --git a/src/server.rs b/src/server.rs
new file mode 100644
index 0000000..4c628d6
--- /dev/null
+++ b/src/server.rs
@@ -0,0 +1,49 @@
+use anyhow::{bail, Result};
+use std::sync::Arc;
+
+use rusoto_signature::Region;
+
+use crate::config::*;
+use crate::login::{ldap_provider::*, static_provider::*, *};
+use crate::mailbox::Mailbox;
+
+pub struct Server {
+ pub login_provider: Box<dyn LoginProvider>,
+}
+
+impl Server {
+ pub fn new(config: Config) -> Result<Arc<Self>> {
+ let s3_region = Region::Custom {
+ name: config.s3_region,
+ endpoint: config.s3_endpoint,
+ };
+ let k2v_region = Region::Custom {
+ name: config.k2v_region,
+ endpoint: config.k2v_endpoint,
+ };
+ let login_provider: Box<dyn LoginProvider> = match (config.login_static, config.login_ldap)
+ {
+ (Some(st), None) => Box::new(StaticLoginProvider::new(st, k2v_region, s3_region)?),
+ (None, Some(ld)) => Box::new(LdapLoginProvider::new(ld, k2v_region, s3_region)?),
+ (Some(_), Some(_)) => bail!("A single login provider must be set up in config file"),
+ (None, None) => bail!("No login provider is set up in config file"),
+ };
+ Ok(Arc::new(Self {
+ login_provider,
+ }))
+ }
+
+ pub async fn run(self: &Arc<Self>) -> Result<()> {
+ let creds = self.login_provider.login("lx", "plop").await?;
+
+ let mut mailbox = Mailbox::new(
+ &creds,
+ "TestMailbox".to_string(),
+ )
+ .await?;
+
+ mailbox.test().await?;
+
+ Ok(())
+ }
+}