aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/util
diff options
context:
space:
mode:
authorbenj <benj@rse8.com>2023-04-24 13:24:45 -0700
committerbenj <benj@rse8.com>2023-04-24 13:24:45 -0700
commiteb92f823c31a5e702af7005231f0d6915aad3342 (patch)
treebb624786a47accb2dfcfe95d20c00c9624c28a9c /crates/secd/src/util
parent176aae037400b43cb3971cd968afe59c73b3097a (diff)
downloadsecdiam-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 'crates/secd/src/util')
-rw-r--r--crates/secd/src/util/crypter.rs87
-rw-r--r--crates/secd/src/util/from.rs5
-rw-r--r--crates/secd/src/util/mod.rs107
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())
+ }
+ _ => {}
+ };
+ }
+}