diff options
| author | benj <benj@rse8.com> | 2023-04-24 13:24:45 -0700 |
|---|---|---|
| committer | benj <benj@rse8.com> | 2023-04-24 13:24:45 -0700 |
| commit | eb92f823c31a5e702af7005231f0d6915aad3342 (patch) | |
| tree | bb624786a47accb2dfcfe95d20c00c9624c28a9c /crates/secd/src/util | |
| parent | 176aae037400b43cb3971cd968afe59c73b3097a (diff) | |
| download | secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.gz secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.bz2 secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.lz secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.xz secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.zst secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.zip | |
email templates, sendgrid, creds, and some experimental things
Started playing with namespace configs and integrating with zanzibar impls. Still lot's of experimenting and dead code going on.
Diffstat (limited to '')
| -rw-r--r-- | crates/secd/src/util/crypter.rs | 87 | ||||
| -rw-r--r-- | crates/secd/src/util/from.rs | 5 | ||||
| -rw-r--r-- | crates/secd/src/util/mod.rs | 107 |
3 files changed, 180 insertions, 19 deletions
diff --git a/crates/secd/src/util/crypter.rs b/crates/secd/src/util/crypter.rs new file mode 100644 index 0000000..1717377 --- /dev/null +++ b/crates/secd/src/util/crypter.rs @@ -0,0 +1,87 @@ +use aes_gcm::{ + aead::{Aead, KeyInit, OsRng}, + Aes256Gcm, Nonce, +}; +use argon2::{ + password_hash::{self, SaltString}, + Argon2, PasswordHasher, +}; +use derive_more::Display; +use rand::Rng; +use sha2::{Digest, Sha256}; +use thiserror::Error; + +#[derive(Debug, Display, Error)] +pub enum CrypterError { + EncryptError(String), + DecryptError(String), + DecodeError(String), + HashError(String), +} + +pub struct Crypter { + pub key: Vec<u8>, +} + +impl Crypter { + pub fn new(key: &[u8]) -> Self { + Self { key: key.to_vec() } + } + + pub fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, CrypterError> { + let mut hasher = Sha256::new(); + hasher.update(&self.key); + + let cipher = Aes256Gcm::new(&hasher.finalize()); + + let rbs = rand::thread_rng().gen::<[u8; 12]>(); + let iv = Nonce::from_slice(&rbs); + let crypt = cipher + .encrypt(&iv, data) + .map_err(|e| CrypterError::EncryptError(e.to_string()))?; + + let mut msg = iv.to_vec(); + msg.extend_from_slice(&crypt); + Ok(msg) + } + + pub fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, CrypterError> { + let mut hasher = Sha256::new(); + hasher.update(&self.key); + + let cipher = Aes256Gcm::new(&hasher.finalize()); + + let iv = Nonce::from_slice(&data[0..=11]); + let data = &data[12..]; + Ok(cipher + .decrypt(&iv, data) + .map_err(|e| CrypterError::DecryptError(e.to_string()))?) + } + + pub fn hash(&self, data: &[u8]) -> Result<String, CrypterError> { + let salt = SaltString::generate(&mut OsRng); + let hasher = Argon2::default(); + Ok(hasher + .hash_password(data, &salt) + .map_err(|e| CrypterError::HashError(e.to_string()))? + .to_string()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn encrypt_data_test() { + let crypter = Crypter { + key: "testkey".to_string().into_bytes(), + }; + + let plaintext = "This is a secret."; + let enc = crypter.encrypt(&plaintext.as_bytes()).unwrap(); + + let res = crypter.decrypt(&enc).unwrap(); + assert_eq!(plaintext, std::str::from_utf8(&res).unwrap()); + } +} diff --git a/crates/secd/src/util/from.rs b/crates/secd/src/util/from.rs index bab8a25..ec5b62d 100644 --- a/crates/secd/src/util/from.rs +++ b/crates/secd/src/util/from.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use crate::AuthStore; impl From<Option<String>> for AuthStore { @@ -23,7 +21,6 @@ mod test { #[test] fn auth_store_from_string() { let in_conn = None; - let a = AuthStore::from(in_conn.clone()); match AuthStore::from(in_conn.clone()) { AuthStore::Sqlite { conn } => assert_eq!(conn, "sqlite::memory:"), r @ _ => assert!( @@ -46,14 +43,12 @@ mod test { } let sqlite_conn = Some("sqlite:///path/to/db.sql".into()); - let a = AuthStore::from(sqlite_conn.clone()); match AuthStore::from(sqlite_conn.clone()) { AuthStore::Sqlite { conn } => assert_eq!(conn, sqlite_conn.unwrap()), r @ _ => assert!(false, "should have parsed as sqlite. Found: {:?}", r), } let sqlite_mem_conn = Some("sqlite:memory:".into()); - let a = AuthStore::from(sqlite_mem_conn.clone()); match AuthStore::from(sqlite_mem_conn.clone()) { AuthStore::Sqlite { conn } => assert_eq!(conn, sqlite_mem_conn.unwrap()), r @ _ => assert!( diff --git a/crates/secd/src/util/mod.rs b/crates/secd/src/util/mod.rs index c26986d..8676f26 100644 --- a/crates/secd/src/util/mod.rs +++ b/crates/secd/src/util/mod.rs @@ -1,21 +1,14 @@ +pub(crate) mod crypter; pub(crate) mod from; -use rand::{thread_rng, Rng}; +use self::crypter::{Crypter, CrypterError}; +use crate::{ + AddressType, Credential, CredentialType, IdentityId, SecdError, Session, SESSION_DURATION, + SESSION_SIZE_BYTES, +}; use sha2::{Digest, Sha256}; +use std::str::from_utf8; use time::OffsetDateTime; -use url::Url; - -use crate::{AddressType, IdentityId, SecdError, Session, SESSION_DURATION, SESSION_SIZE_BYTES}; - -pub(crate) fn remove_trailing_slash(url: &mut Url) -> String { - let mut u = url.to_string(); - - if u.ends_with('/') { - u.pop(); - } - - u -} pub(crate) fn hash(i: &[u8]) -> Vec<u8> { let mut hasher = Sha256::new(); @@ -51,3 +44,89 @@ impl Session { }) } } + +impl Credential { + pub(crate) fn encrypt(&mut self, crypter: &Crypter) -> Result<(), SecdError> { + Ok(match self.t { + CredentialType::Passphrase { + key: _, + ref mut value, + } => { + *value = hex::encode(crypter.encrypt(value.as_bytes())?); + } + _ => {} + }) + } + pub(crate) fn decrypt(&mut self, crypter: &Crypter) -> Result<(), SecdError> { + Ok(match self.t { + CredentialType::Passphrase { + key: _, + ref mut value, + } => { + *value = from_utf8( + &crypter.decrypt( + &hex::decode(value.clone()) + .map_err(|e| CrypterError::DecodeError(e.to_string()))?, + )?, + ) + .map_err(|e| CrypterError::DecodeError(e.to_string()))? + .to_string() + } + _ => {} + }) + } + + pub(crate) fn hash(&mut self, crypter: &Crypter) -> Result<(), SecdError> { + Ok(match self.t { + CredentialType::Passphrase { + key: _, + ref mut value, + } => { + *value = crypter.hash(value.as_bytes())?; + } + _ => {} + }) + } +} + +#[cfg(test)] +mod test { + use uuid::Uuid; + + use super::*; + + #[test] + fn test_credential_encrypt() { + let c = Crypter::new("AMAZING_KEY".as_bytes()); + + let plaintext_secret = "super_password".to_string(); + + let mut credential = Credential { + id: Uuid::new_v4(), + identity_id: Uuid::new_v4(), + t: CredentialType::Passphrase { + key: "super_user".into(), + value: plaintext_secret.clone(), + }, + created_at: OffsetDateTime::now_utc(), + revoked_at: None, + deleted_at: None, + }; + + credential.encrypt(&c).unwrap(); + match &credential.t { + CredentialType::Passphrase { key: _, value } => { + assert_ne!(plaintext_secret.clone(), value.clone()) + } + _ => {} + }; + + credential.decrypt(&c).unwrap(); + match &credential.t { + CredentialType::Passphrase { key: _, value } => { + assert_eq!(plaintext_secret.clone(), value.clone()) + } + _ => {} + }; + } +} |
