start switching crypto implementations

This commit is contained in:
2023-05-07 21:14:07 -07:00
parent e866a4a643
commit 663bbfa2f3
3 changed files with 212 additions and 23 deletions

View File

@ -2,6 +2,25 @@ use std::fmt::{self, Formatter};
use std::time::{SystemTime, UNIX_EPOCH};
use aws_smithy_types::date_time::{DateTime, Format};
use argon2::{
Argon2,
Algorithm,
Version,
ParamsBuilder,
password_hash::rand_core::{RngCore, OsRng},
};
use chacha20poly1305::{
XChaCha20Poly1305,
XNonce,
aead::{
Aead,
AeadCore,
Key,
KeyInit,
Error as AeadError,
generic_array::GenericArray,
},
};
use serde::{
Serialize,
Deserialize,
@ -10,12 +29,7 @@ use serde::{
};
use serde::de::{self, Visitor};
use sqlx::SqlitePool;
use sodiumoxide::crypto::{
pwhash,
pwhash::Salt,
secretbox,
secretbox::{Nonce, Key}
};
use crate::errors::*;
@ -76,8 +90,8 @@ impl Session {
pub struct LockedCredentials {
pub access_key_id: String,
pub secret_key_enc: Vec<u8>,
pub salt: Salt,
pub nonce: Nonce,
pub salt: [u8; 32],
pub nonce: XNonce,
}
impl LockedCredentials {
@ -97,10 +111,8 @@ impl LockedCredentials {
}
pub fn decrypt(&self, passphrase: &str) -> Result<BaseCredentials, UnlockError> {
let mut key_buf = [0; secretbox::KEYBYTES];
// pretty sure this only fails if we're out of memory
pwhash::derive_key_interactive(&mut key_buf, passphrase.as_bytes(), &self.salt).unwrap();
let decrypted = secretbox::open(&self.secret_key_enc, &self.nonce, &Key(key_buf))
let crypto = Crypto::new(passphrase, &self.salt);
let decrypted = crypto.decrypt(&self.nonce, &self.secret_key_enc)
.map_err(|_| UnlockError::BadPassphrase)?;
let secret_access_key = String::from_utf8(decrypted)
.map_err(|_| UnlockError::InvalidUtf8)?;
@ -122,21 +134,18 @@ pub struct BaseCredentials {
}
impl BaseCredentials {
pub fn encrypt(&self, passphrase: &str) -> LockedCredentials {
let salt = pwhash::gen_salt();
let mut key_buf = [0; secretbox::KEYBYTES];
pwhash::derive_key_interactive(&mut key_buf, passphrase.as_bytes(), &salt).unwrap();
let key = Key(key_buf);
let nonce = secretbox::gen_nonce();
pub fn encrypt(&self, passphrase: &str) -> Result<CryptoError, LockedCredentials> {
let salt = Crypto::salt();
let crypto = Crypto::new(passphrase, &salt)?;
let (nonce, secret_key_enc) = crypto.encrypt(self.secret_access_key.as_bytes());
let secret_key_enc = secretbox::seal(self.secret_access_key.as_bytes(), &nonce, &key);
LockedCredentials {
let locked = LockedCredentials {
access_key_id: self.access_key_id.clone(),
secret_key_enc,
salt,
nonce,
}
};
Ok(locked)
}
}
@ -241,4 +250,60 @@ fn deserialize_expiration<'de, D>(deserializer: D) -> Result<DateTime, D::Error>
where D: Deserializer<'de>
{
deserializer.deserialize_str(DateTimeVisitor)
}
}
struct Crypto {
cipher: XChaCha20Poly1305,
}
impl Crypto {
/// Argon2 params rationale:
///
/// m_cost is measured in KiB, so 128 * 1024 gives us 128MiB.
/// This should roughly double the memory usage of the application
/// while deriving the key.
///
/// p_cost is irrelevant since (at present) there isn't any parallelism
/// implemented, so we leave it at 1.
///
/// With the above m_cost, t_cost = 8 results in about 800ms to derive
/// 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.
fn new(passphrase: &str, salt: &[u8]) -> argon2::Result<Crypto> {
let params = ParamsBuilder::new()
.m_cost(128 * 1024)
.p_cost(1)
.t_cost(8)
.build()
.unwrap(); // only errors if the given params are invalid
let hasher = Argon2::new(
Algorithm::Argon2id,
Version::V0x13,
params,
);
let mut key = [0; 32];
hasher.hash_password_into(passphrase.as_bytes(), &salt, &mut key)?;
let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&key));
Ok(Crypto { cipher })
}
fn salt() -> [u8; 32] {
let mut salt = [0; 32];
OsRng.fill_bytes(&mut salt);
salt
}
fn encrypt(&self, data: &[u8]) -> Result<(XNonce, Vec<u8>), AeadError> {
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)
}
}