start switching crypto implementations
This commit is contained in:
		@@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user