get backend running

This commit is contained in:
2024-06-19 05:10:55 -04:00
parent d0a2532c27
commit 9928996fab
11 changed files with 310 additions and 207 deletions

View File

@@ -1,3 +1,5 @@
use std::fmt::{Debug, Formatter};
use argon2::{
Argon2,
Algorithm,
@@ -12,32 +14,25 @@ use chacha20poly1305::{
Aead,
AeadCore,
KeyInit,
Error as AeadError,
generic_array::GenericArray,
},
};
use serde::{Serialize, Deserialize};
use sqlx::{FromRow, SqlitePool};
use serde::Deserialize;
use sqlx::SqlitePool;
use crate::errors::*;
use crate::kv;
mod aws;
pub use aws::{AwsBaseCredential, AwsSessionCredential};
pub enum CredentialKind {
AwsBase,
AwsSession,
}
pub trait PersistentCredential {
pub trait PersistentCredential: for<'a> Deserialize<'a> + Sized {
async fn load(crypt: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError>;
async fn save(&self, crypt: &Crypto, pool: &SqlitePool) -> Result<(), SaveCredentialsError>;
}
#[derive(Debug, Clone)]
#[derive(Clone, Debug)]
pub enum AppSession {
Unlocked {
salt: [u8; 32],
@@ -54,14 +49,14 @@ pub enum AppSession {
impl AppSession {
pub fn new(passphrase: &str) -> Result<Self, CryptoError> {
let salt = Crypto::salt();
let crypto = Crypto::new(passphrase, &salt);
let crypto = Crypto::new(passphrase, &salt)?;
Ok(Self::Unlocked {salt, crypto})
}
pub fn unlock(self, passphrase: &str) -> Result<Self, UnlockError> {
pub fn unlock(&mut self, passphrase: &str) -> Result<(), UnlockError> {
let (salt, nonce, blob) = match self {
Self::Empty => return Err(UnlockError::NoCredentials),
Self::Unlocked => return Err(UnlockError::NotLocked),
Self::Unlocked {..} => return Err(UnlockError::NotLocked),
Self::Locked {salt, verify_nonce, verify_blob} => (salt, verify_nonce, verify_blob),
};
@@ -69,61 +64,78 @@ impl AppSession {
.map_err(|e| CryptoError::Argon2(e))?;
// if passphrase is incorrect, this will fail
let verify = crypto.decrypt(&nonce, &blob)?;
let _verify = crypto.decrypt(&nonce, &blob)?;
Ok(Self::Unlocked{crypto, salt})
*self = Self::Unlocked {crypto, salt: *salt};
Ok(())
}
pub async fn load(pool: &SqlitePool) -> Result<Self, LoadKvError> {
pub async fn load(pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
match kv::load_bytes_multi!(pool, "salt", "verify_nonce", "verify_blob").await? {
Some((salt, verify_nonce, verify_blob)) => {
Ok(Self::Locked {salt, verify_nonce, verify_blob}),
Some((salt, nonce, blob)) => {
Ok(Self::Locked {
salt: salt.try_into().map_err(|_| LoadCredentialsError::InvalidData)?,
// note: replace this with try_from at some point
verify_nonce: XNonce::clone_from_slice(&nonce),
verify_blob: blob,
})
},
None => Ok(Self::Empty),
}
}
pub async fn save(&self, pool: &SqlitePool) -> Result<(), LockError> {
let (salt, nonce, blob) = match self {
pub async fn save(&self, pool: &SqlitePool) -> Result<(), SaveCredentialsError> {
match self {
Self::Unlocked {salt, crypto} => {
let (nonce, blob) = crypto.encrypt(b"correct horse battery staple")
.map_err(|e| CryptoError::Aead(e))?;
(salt, nonce, blob)
let (nonce, blob) = crypto.encrypt(b"correct horse battery staple")?;
kv::save(pool, "salt", salt).await?;
kv::save(pool, "verify_nonce", &nonce.as_slice()).await?;
kv::save(pool, "verify_blob", &blob).await?;
},
Self::Locked {salt, verify_nonce, verify_blob} => {
kv::save(pool, "salt", salt).await?;
kv::save(pool, "verify_nonce", &verify_nonce.as_slice()).await?;
kv::save(pool, "verify_blob", verify_blob).await?;
},
Self::Locked {salt, verify_nonce, verify_blob} => (salt, verify_nonce, verify_blob),
// "saving" an empty session just means doing nothing
Self::Empty => return Ok(()),
Self::Empty => (),
};
kv::save(pool, "salt", salt).await?;
kv::save(pool, "verify_nonce", nonce).await?;
kv::save(pool, "verify_blob", blob).await?;
Ok(())
}
pub fn try_encrypt(&self, data: &[u8]) -> Result<(XNonce, Vec<u8>), CryptoError> {
let crypto = match self {
pub fn try_get_crypto(&self) -> Result<&Crypto, GetCredentialsError> {
match self {
Self::Empty => Err(GetCredentialsError::Empty),
Self::Locked => Err(GetCredentialsError::Locked),
Self::Locked {..} => Err(GetCredentialsError::Locked),
Self::Unlocked {crypto, ..} => Ok(crypto),
}
}
pub fn try_encrypt(&self, data: &[u8]) -> Result<(XNonce, Vec<u8>), GetCredentialsError> {
let crypto = match self {
Self::Empty => return Err(GetCredentialsError::Empty),
Self::Locked {..} => return Err(GetCredentialsError::Locked),
Self::Unlocked {crypto, ..} => crypto,
}?;
};
let res = crypto.encrypt(data)?;
Ok(res)
}
pub fn try_decrypt(&self, nonce: XNonce, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
pub fn try_decrypt(&self, nonce: XNonce, data: &[u8]) -> Result<Vec<u8>, GetCredentialsError> {
let crypto = match self {
Self::Empty => Err(GetCredentialsError::Empty),
Self::Locked => Err(GetCredentialsError::Locked),
Self::Empty => return Err(GetCredentialsError::Empty),
Self::Locked {..} => return Err(GetCredentialsError::Locked),
Self::Unlocked {crypto, ..} => crypto,
}?;
let res = crypto.decrypt(nonce, data)?;
};
let res = crypto.decrypt(&nonce, data)?;
Ok(res)
}
}
#[derive(Clone)]
pub struct Crypto {
cipher: XChaCha20Poly1305,
}
@@ -181,13 +193,20 @@ impl Crypto {
salt
}
fn encrypt(&self, data: &[u8]) -> Result<(XNonce, Vec<u8>), AeadError> {
fn encrypt(&self, data: &[u8]) -> Result<(XNonce, Vec<u8>), CryptoError> {
let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng);
let ciphertext = self.cipher.encrypt(&nonce, data)?;
Ok((nonce, ciphertext))
}
fn decrypt(&self, nonce: &XNonce, data: &[u8]) -> Result<Vec<u8>, AeadError> {
self.cipher.decrypt(nonce, data)
fn decrypt(&self, nonce: &XNonce, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
let plaintext = self.cipher.decrypt(nonce, data)?;
Ok(plaintext)
}
}
impl Debug for Crypto {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "Crypto {{ [...] }}")
}
}