aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/util
diff options
context:
space:
mode:
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())
+ }
+ _ => {}
+ };
+ }
+}