aerogramme/aero-user/src/login/mod.rs

246 lines
8.8 KiB
Rust
Raw Normal View History

2023-12-29 16:16:41 +00:00
pub mod demo_provider;
2022-05-19 10:10:48 +00:00
pub mod ldap_provider;
pub mod static_provider;
2023-12-27 13:58:28 +00:00
use std::sync::Arc;
2022-05-20 11:36:45 +00:00
use anyhow::{anyhow, bail, Context, Result};
2022-05-19 10:10:48 +00:00
use async_trait::async_trait;
2024-03-08 07:17:03 +00:00
use base64::Engine;
2022-05-20 10:49:53 +00:00
use rand::prelude::*;
2022-05-19 10:10:48 +00:00
2022-05-19 12:33:49 +00:00
use crate::cryptoblob::*;
2023-11-01 14:36:06 +00:00
use crate::storage::*;
2022-05-19 10:10:48 +00:00
/// The trait LoginProvider defines the interface for a login provider that allows
/// to retrieve storage and cryptographic credentials for access to a user account
/// from their username and password.
2022-05-19 11:54:38 +00:00
#[async_trait]
pub trait LoginProvider {
/// The login method takes an account's password as an input to decypher
/// decryption keys and obtain full access to the user's account.
async fn login(&self, username: &str, password: &str) -> Result<Credentials>;
2022-05-31 13:30:32 +00:00
/// The public_login method takes an account's email address and returns
/// public credentials for adding mails to the user's inbox.
async fn public_login(&self, email: &str) -> Result<PublicCredentials>;
2022-05-19 11:54:38 +00:00
}
2022-06-17 16:39:36 +00:00
/// ArcLoginProvider is simply an alias on a structure that is used
/// in many places in the code
pub type ArcLoginProvider = Arc<dyn LoginProvider + Send + Sync>;
/// The struct Credentials represent all of the necessary information to interact
/// with a user account's data after they are logged in.
2022-05-19 10:10:48 +00:00
#[derive(Clone, Debug)]
pub struct Credentials {
/// The storage credentials are used to authenticate access to the underlying storage (S3, K2V)
2023-12-18 16:09:44 +00:00
pub storage: Builder,
/// The cryptographic keys are used to encrypt and decrypt data stored in S3 and K2V
2022-05-19 12:33:49 +00:00
pub keys: CryptoKeys,
}
2022-05-31 13:30:32 +00:00
#[derive(Clone, Debug)]
pub struct PublicCredentials {
/// The storage credentials are used to authenticate access to the underlying storage (S3, K2V)
2023-12-18 16:09:44 +00:00
pub storage: Builder,
2022-05-31 13:30:32 +00:00
pub public_key: PublicKey,
}
2023-12-27 13:58:28 +00:00
use serde::{Deserialize, Serialize};
2023-12-13 15:09:01 +00:00
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CryptoRoot(pub String);
impl CryptoRoot {
pub fn create_pass(password: &str, k: &CryptoKeys) -> Result<Self> {
let bytes = k.password_seal(password)?;
let b64 = base64::engine::general_purpose::STANDARD_NO_PAD.encode(bytes);
let cr = format!("aero:cryptoroot:pass:{}", b64);
Ok(Self(cr))
}
pub fn create_cleartext(k: &CryptoKeys) -> Self {
let bytes = k.serialize();
let b64 = base64::engine::general_purpose::STANDARD_NO_PAD.encode(bytes);
let cr = format!("aero:cryptoroot:cleartext:{}", b64);
Self(cr)
}
pub fn create_incoming(pk: &PublicKey) -> Self {
let bytes: &[u8] = &pk[..];
let b64 = base64::engine::general_purpose::STANDARD_NO_PAD.encode(bytes);
let cr = format!("aero:cryptoroot:incoming:{}", b64);
Self(cr)
}
pub fn public_key(&self) -> Result<PublicKey> {
match self.0.splitn(4, ':').collect::<Vec<&str>>()[..] {
2023-12-27 13:58:28 +00:00
["aero", "cryptoroot", "pass", b64blob] => {
2023-12-13 15:09:01 +00:00
let blob = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64blob)?;
if blob.len() < 32 {
2023-12-27 13:58:28 +00:00
bail!(
"Decoded data is {} bytes long, expect at least 32 bytes",
blob.len()
);
2023-12-13 15:09:01 +00:00
}
PublicKey::from_slice(&blob[..32]).context("must be a valid public key")
2023-12-27 13:58:28 +00:00
}
["aero", "cryptoroot", "cleartext", b64blob] => {
2023-12-13 15:09:01 +00:00
let blob = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64blob)?;
Ok(CryptoKeys::deserialize(&blob)?.public)
2023-12-27 13:58:28 +00:00
}
["aero", "cryptoroot", "incoming", b64blob] => {
2023-12-13 15:09:01 +00:00
let blob = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64blob)?;
if blob.len() < 32 {
2023-12-27 13:58:28 +00:00
bail!(
"Decoded data is {} bytes long, expect at least 32 bytes",
blob.len()
);
2023-12-13 15:09:01 +00:00
}
PublicKey::from_slice(&blob[..32]).context("must be a valid public key")
2023-12-27 13:58:28 +00:00
}
["aero", "cryptoroot", "keyring", _] => {
2023-12-13 15:09:01 +00:00
bail!("keyring is not yet implemented!")
2023-12-27 13:58:28 +00:00
}
_ => bail!(format!(
"passed string '{}' is not a valid cryptoroot",
self.0
)),
2023-12-13 15:09:01 +00:00
}
}
pub fn crypto_keys(&self, password: &str) -> Result<CryptoKeys> {
match self.0.splitn(4, ':').collect::<Vec<&str>>()[..] {
2023-12-27 13:58:28 +00:00
["aero", "cryptoroot", "pass", b64blob] => {
2023-12-13 15:09:01 +00:00
let blob = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64blob)?;
2023-12-13 17:04:04 +00:00
CryptoKeys::password_open(password, &blob)
2023-12-27 13:58:28 +00:00
}
["aero", "cryptoroot", "cleartext", b64blob] => {
2023-12-13 15:09:01 +00:00
let blob = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64blob)?;
CryptoKeys::deserialize(&blob)
2023-12-27 13:58:28 +00:00
}
["aero", "cryptoroot", "incoming", _] => {
2023-12-13 15:09:01 +00:00
bail!("incoming cryptoroot does not contain a crypto key!")
2023-12-27 13:58:28 +00:00
}
["aero", "cryptoroot", "keyring", _] => {
2023-12-13 15:09:01 +00:00
bail!("keyring is not yet implemented!")
2023-12-27 13:58:28 +00:00
}
_ => bail!(format!(
"passed string '{}' is not a valid cryptoroot",
self.0
)),
2023-12-13 15:09:01 +00:00
}
}
}
/// The struct CryptoKeys contains the cryptographic keys used to encrypt and decrypt
/// data in a user's mailbox.
2022-05-19 12:33:49 +00:00
#[derive(Clone, Debug)]
pub struct CryptoKeys {
/// Master key for symmetric encryption of mailbox data
2022-05-19 12:33:49 +00:00
pub master: Key,
/// Public/private keypair for encryption of incomming emails (secret part)
2022-05-19 12:33:49 +00:00
pub secret: SecretKey,
/// Public/private keypair for encryption of incomming emails (public part)
2022-05-19 12:33:49 +00:00
pub public: PublicKey,
}
// ----
impl CryptoKeys {
2023-12-13 15:09:01 +00:00
/// Initialize a new cryptography root
pub fn init() -> Self {
2022-05-20 10:49:53 +00:00
let (public, secret) = gen_keypair();
let master = gen_key();
2023-12-13 15:09:01 +00:00
CryptoKeys {
2022-05-20 10:49:53 +00:00
master,
secret,
public,
}
}
2023-12-13 15:09:01 +00:00
// Clear text serialize/deserialize
/// Serialize the root as bytes without encryption
2022-05-20 10:49:53 +00:00
fn serialize(&self) -> [u8; 64] {
let mut res = [0u8; 64];
res[..32].copy_from_slice(self.master.as_ref());
res[32..].copy_from_slice(self.secret.as_ref());
res
}
2023-12-13 15:09:01 +00:00
/// Deserialize a clear text crypto root without encryption
2022-05-20 10:49:53 +00:00
fn deserialize(bytes: &[u8]) -> Result<Self> {
if bytes.len() != 64 {
bail!("Invalid length: {}, expected 64", bytes.len());
}
let master = Key::from_slice(&bytes[..32]).unwrap();
let secret = SecretKey::from_slice(&bytes[32..]).unwrap();
let public = secret.public_key();
Ok(Self {
master,
secret,
public,
})
}
2023-12-13 15:09:01 +00:00
// Password sealed keys serialize/deserialize
pub fn password_open(password: &str, blob: &[u8]) -> Result<Self> {
2023-12-13 17:04:04 +00:00
let _pubkey = &blob[0..32];
let kdf_salt = &blob[32..64];
let password_openned = try_open_encrypted_keys(kdf_salt, password, &blob[64..])?;
2023-12-13 15:09:01 +00:00
let keys = Self::deserialize(&password_openned)?;
Ok(keys)
}
pub fn password_seal(&self, password: &str) -> Result<Vec<u8>> {
let mut kdf_salt = [0u8; 32];
thread_rng().fill(&mut kdf_salt);
// Calculate key for password secret box
let password_key = derive_password_key(&kdf_salt, password)?;
// Seal a secret box that contains our crypto keys
let password_sealed = seal(&self.serialize(), &password_key)?;
// Create blob
let password_blob = [&self.public[..], &kdf_salt[..], &password_sealed].concat();
Ok(password_blob)
}
2022-05-20 10:49:53 +00:00
}
2023-12-06 19:57:25 +00:00
fn derive_password_key(kdf_salt: &[u8], password: &str) -> Result<Key> {
Ok(Key::from_slice(&argon2_kdf(kdf_salt, password.as_bytes(), 32)?).unwrap())
}
2023-12-27 13:58:28 +00:00
fn try_open_encrypted_keys(
kdf_salt: &[u8],
password: &str,
encrypted_keys: &[u8],
) -> Result<Vec<u8>> {
2023-12-06 19:57:25 +00:00
let password_key = derive_password_key(kdf_salt, password)?;
open(encrypted_keys, &password_key)
}
2022-05-20 10:49:53 +00:00
// ---- UTIL ----
pub fn argon2_kdf(salt: &[u8], password: &[u8], output_len: usize) -> Result<Vec<u8>> {
2023-12-27 13:58:28 +00:00
use argon2::{password_hash, Algorithm, Argon2, ParamsBuilder, PasswordHasher, Version};
2022-05-20 10:49:53 +00:00
2023-12-21 19:23:43 +00:00
let params = ParamsBuilder::new()
2022-05-20 10:49:53 +00:00
.output_len(output_len)
2023-12-21 19:23:43 +00:00
.build()
2022-05-20 10:49:53 +00:00
.map_err(|e| anyhow!("Invalid argon2 params: {}", e))?;
let argon2 = Argon2::new(Algorithm::default(), Version::default(), params);
2023-12-21 19:23:43 +00:00
let b64_salt = base64::engine::general_purpose::STANDARD_NO_PAD.encode(salt);
2023-12-27 13:58:28 +00:00
let valid_salt = password_hash::Salt::from_b64(&b64_salt)
.map_err(|e| anyhow!("Invalid salt, error {}", e))?;
2022-05-20 10:49:53 +00:00
let hash = argon2
2023-12-21 19:23:43 +00:00
.hash_password(password, valid_salt)
2022-05-20 10:49:53 +00:00
.map_err(|e| anyhow!("Unable to hash: {}", e))?;
let hash = hash.hash.ok_or(anyhow!("Missing output"))?;
assert!(hash.len() == output_len);
Ok(hash.as_bytes().to_vec())
}