From d650909ac73cbe12c9d72d0ab27b21db8ba7e7bd Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Mon, 8 May 2023 22:11:58 -0700 Subject: [PATCH] switch crypto implementation and add spinner --- package.json | 2 +- src-tauri/Cargo.toml | 5 +- src-tauri/src/credentials.rs | 44 +++++++----- src-tauri/src/errors.rs | 13 +++- src-tauri/src/state.rs | 2 +- src/App.svelte | 1 - src/ui/Spinner.svelte | 113 ++++++++++++++++++++++++++++++ src/views/EnterCredentials.svelte | 13 +++- src/views/Unlock.svelte | 14 +++- 9 files changed, 181 insertions(+), 26 deletions(-) create mode 100644 src/ui/Spinner.svelte diff --git a/package.json b/package.json index 9328533..7b9b6f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "creddy", - "version": "0.2.0", + "version": "0.2.1", "scripts": { "dev": "vite", "build": "vite build", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7d2924d..62f654d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -36,9 +36,8 @@ auto-launch = "0.4.0" dirs = "5.0" clap = { version = "3.2.23", features = ["derive"] } is-terminal = "0.4.7" -argon2 = "0.5.0" -chacha20poly1305 = "0.10.1" -generic_array = "0.14.6" +argon2 = { version = "0.5.0", features = ["std"] } +chacha20poly1305 = { version = "0.10.1", features = ["std"] } [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/credentials.rs b/src-tauri/src/credentials.rs index f15e417..5a18aed 100644 --- a/src-tauri/src/credentials.rs +++ b/src-tauri/src/credentials.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Formatter}; use std::time::{SystemTime, UNIX_EPOCH}; -use aws_smithy_types::date_time::{DateTime, Format}; + use aws_smithy_types::date_time::{DateTime, Format}; use argon2::{ Argon2, Algorithm, @@ -15,7 +15,6 @@ use chacha20poly1305::{ aead::{ Aead, AeadCore, - Key, KeyInit, Error as AeadError, generic_array::GenericArray, @@ -54,18 +53,17 @@ impl Session { None => {return Ok(Session::Empty);} }; - let salt_buf: [u8; 32] = row.salt - .try_into() - .map_err(|_e| SetupError::InvalidRecord)?; - let nonce_buf: [u8; 24] = row.nonce + let salt: [u8; 32] = row.salt .try_into() .map_err(|_e| SetupError::InvalidRecord)?; + let nonce = XNonce::from_exact_iter(row.nonce.into_iter()) + .ok_or(SetupError::InvalidRecord)?; let creds = LockedCredentials { access_key_id: row.access_key_id, secret_key_enc: row.secret_key_enc, - salt: Salt(salt_buf), - nonce: Nonce(nonce_buf), + salt, + nonce, }; Ok(Session::Locked(creds)) } @@ -102,8 +100,8 @@ impl LockedCredentials { ) .bind(&self.access_key_id) .bind(&self.secret_key_enc) - .bind(&self.salt.0[0..]) - .bind(&self.nonce.0[0..]) + .bind(&self.salt[..]) + .bind(&self.nonce[..]) .execute(pool) .await?; @@ -111,9 +109,10 @@ impl LockedCredentials { } pub fn decrypt(&self, passphrase: &str) -> Result { - let crypto = Crypto::new(passphrase, &self.salt); + let crypto = Crypto::new(passphrase, &self.salt) + .map_err(|e| CryptoError::Argon2(e))?; let decrypted = crypto.decrypt(&self.nonce, &self.secret_key_enc) - .map_err(|_| UnlockError::BadPassphrase)?; + .map_err(|e| CryptoError::Aead(e))?; let secret_access_key = String::from_utf8(decrypted) .map_err(|_| UnlockError::InvalidUtf8)?; @@ -134,10 +133,10 @@ pub struct BaseCredentials { } impl BaseCredentials { - pub fn encrypt(&self, passphrase: &str) -> Result { + pub fn encrypt(&self, passphrase: &str) -> Result { let salt = Crypto::salt(); let crypto = Crypto::new(passphrase, &salt)?; - let (nonce, secret_key_enc) = crypto.encrypt(self.secret_access_key.as_bytes()); + let (nonce, secret_key_enc) = crypto.encrypt(self.secret_access_key.as_bytes())?; let locked = LockedCredentials { access_key_id: self.access_key_id.clone(), @@ -271,11 +270,24 @@ impl Crypto { /// a key on my (somewhat older) CPU. This is probably overkill, but /// given that it should only have to happen ~once a day for most /// usage, it should be acceptable. + #[cfg(not(debug_assertions))] + const MEM_COST: u32 = 128 * 1024; + #[cfg(not(debug_assertions))] + const TIME_COST: u32 = 8; + + /// But since this takes a million years in an unoptimized build, + /// we turn it way down in debug builds. + #[cfg(debug_assertions)] + const MEM_COST: u32 = 48 * 1024; + #[cfg(debug_assertions)] + const TIME_COST: u32 = 1; + + fn new(passphrase: &str, salt: &[u8]) -> argon2::Result { let params = ParamsBuilder::new() - .m_cost(128 * 1024) + .m_cost(Self::MEM_COST) .p_cost(1) - .t_cost(8) + .t_cost(Self::TIME_COST) .build() .unwrap(); // only errors if the given params are invalid diff --git a/src-tauri/src/errors.rs b/src-tauri/src/errors.rs index 7d014f0..ae91ac5 100644 --- a/src-tauri/src/errors.rs +++ b/src-tauri/src/errors.rs @@ -164,8 +164,8 @@ pub enum UnlockError { NotLocked, #[error("No saved credentials were found")] NoCredentials, - #[error("Invalid passphrase")] - BadPassphrase, + #[error(transparent)] + Crypto(#[from] CryptoError), #[error("Data was found to be corrupt after decryption")] InvalidUtf8, // Somehow we got invalid utf-8 even though decryption succeeded #[error("Database error: {0}")] @@ -175,6 +175,15 @@ pub enum UnlockError { } +#[derive(Debug, ThisError, AsRefStr)] +pub enum CryptoError { + #[error(transparent)] + Argon2(#[from] argon2::Error), + #[error("Invalid passphrase")] // I think this is the only way decryption fails + Aead(#[from] chacha20poly1305::aead::Error), +} + + // Errors encountered while trying to figure out who's on the other end of a request #[derive(Debug, ThisError, AsRefStr)] pub enum ClientInfoError { diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 6ceae38..eaaaed2 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -48,7 +48,7 @@ impl AppState { } pub async fn new_creds(&self, base_creds: BaseCredentials, passphrase: &str) -> Result<(), UnlockError> { - let locked = base_creds.encrypt(passphrase); + let locked = base_creds.encrypt(passphrase)?; // do this first so that if it fails we don't save bad credentials self.new_session(base_creds).await?; locked.save(&self.pool).await?; diff --git a/src/App.svelte b/src/App.svelte index 1a96d9f..4f0d979 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -15,7 +15,6 @@ invoke('get_config').then(config => $appState.config = config); listen('credentials-request', (tauriEvent) => { $appState.pendingRequests.put(tauriEvent.payload); }); -window.state = $appState; acceptRequest(); diff --git a/src/ui/Spinner.svelte b/src/ui/Spinner.svelte new file mode 100644 index 0000000..6931b3f --- /dev/null +++ b/src/ui/Spinner.svelte @@ -0,0 +1,113 @@ + + + + + +
+
+
+
+
+
diff --git a/src/views/EnterCredentials.svelte b/src/views/EnterCredentials.svelte index 9f8cf50..cfecaa8 100644 --- a/src/views/EnterCredentials.svelte +++ b/src/views/EnterCredentials.svelte @@ -7,6 +7,7 @@ import { navigate } from '../lib/routing.js'; import Link from '../ui/Link.svelte'; import ErrorAlert from '../ui/ErrorAlert.svelte'; + import Spinner from '../ui/Spinner.svelte'; let errorMsg = null; @@ -19,6 +20,7 @@ } } + let saving = false; async function save() { if (passphrase !== confirmPassphrase) { alert.shake(); @@ -27,6 +29,7 @@ let credentials = {AccessKeyId, SecretAccessKey}; try { + saving = true; await invoke('save_credentials', {credentials, passphrase}); if ($appState.currentRequest) { navigate('Approve'); @@ -47,6 +50,8 @@ if (alert) { alert.shake(); } + + saving = false; } } @@ -65,7 +70,13 @@ - + diff --git a/src/views/Unlock.svelte b/src/views/Unlock.svelte index 3609e4d..637da20 100644 --- a/src/views/Unlock.svelte +++ b/src/views/Unlock.svelte @@ -7,12 +7,14 @@ import { getRootCause } from '../lib/errors.js'; import ErrorAlert from '../ui/ErrorAlert.svelte'; import Link from '../ui/Link.svelte'; + import Spinner from '../ui/Spinner.svelte'; let errorMsg = null; let alert; let passphrase = ''; let loadTime = 0; + let saving = false; async function unlock() { // The hotkey for navigating here from homepage is Enter, which also // happens to trigger the form submit event @@ -21,6 +23,7 @@ } try { + saving = true; let r = await invoke('unlock', {passphrase}); $appState.credentialStatus = 'unlocked'; if ($appState.currentRequest) { @@ -43,6 +46,8 @@ if (alert) { alert.shake(); } + + saving = true; } } @@ -62,7 +67,14 @@ - + +