get backend running
This commit is contained in:
@@ -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 {{ [...] }}")
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user