Storing Data
Because having an intern enter secrets into a spreadsheet would mean I'd have to write a job description.
pub mod models;pub mod secret;use std::str::{from_utf8, Utf8Error};
// 1
use aes_gcm::{
aead::{Aead, KeyInit, OsRng},
Aes256Gcm,
Nonce,
};
// 2
use argon2::{
password_hash::{PasswordHash, PasswordHasher, SaltString},
Argon2,
};
use chrono::{DateTime, Utc};
use serde::Serialize;
use sqlx::{FromRow, Pool, Postgres};
use uuid::Uuid;
// 3
#[derive(FromRow, Debug)]
pub struct Secret {
pub secret_id: Uuid,
pub encoded_secret: String,
pub salt: String,
pub created_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>,
}
// 4
#[derive(Debug)]
pub enum SecretError {
HashingError,
SqlError(sqlx::Error),
DecodingError(Utf8Error),
}
impl Secret {
pub async fn insert_secret(
pool: &Pool<Postgres>,
secret: &str,
code: &str,
expires_at: DateTime<Utc>,
) -> Result<Secret, SecretError> {
// 5
let salt = SaltString::generate(&mut OsRng);
let password_hash = Argon2::default()
.hash_password(code.as_bytes(), &salt)
.map_err(|_e| SecretError::HashingError)?
.to_string();
// 6
let parsed_hash =
PasswordHash::new(&password_hash).map_err(|_e| SecretError::HashingError)?;
let cipher = Aes256Gcm::new_from_slice(&parsed_hash.hash.unwrap().as_bytes())
.map_err(|_e| SecretError::HashingError)?;
let nonce = Nonce::from_slice(b"012345678912");
// 7
let ciphertext = cipher
.encrypt(nonce, secret.as_bytes())
.map_err(|_e| SecretError::HashingError)?;
// 8
sqlx::query_as(
"
INSERT INTO secrets (encoded_secret, salt, expires_at)
VALUES ($1, $2, $3)
RETURNING secret_id, encoded_secret, salt, created_at, expires_at",
)
.bind(&base64::encode(ciphertext))
.bind(&salt.to_string())
.bind(&expires_at)
.fetch_one(pool)
.await
.map_err(|e| SecretError::SqlError(e))
}
}
Last updated