use crate::crypto::{AuthSecret, Secret, AUTH_SECRET_LEN, SECRET_LEN};
use async_std::fs::{File, OpenOptions};
use async_std::io::Error;
use async_std::path::{Path, PathBuf};
use async_std::prelude::*;
use async_std::task;
use core::ops::Deref;
use rand::{thread_rng, Rng};
use strobe_rs::{SecParam, Strobe};
async fn inner_read(path: &Path, buf: &mut [u8]) -> Result<(), Error> {
let mut file = File::open(path).await?;
file.read_exact(buf).await?;
Ok(())
}
async fn inner_write(path: &Path, buf: &[u8]) -> Result<(), Error> {
let mut file = File::create(path).await?;
#[cfg(unix)]
{
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
file.set_permissions(Permissions::from_mode(0o600)).await?;
}
file.write_all(buf).await?;
file.sync_all().await?;
Ok(())
}
pub struct SecretFile(PathBuf);
impl SecretFile {
pub fn new(path: PathBuf) -> Self {
Self(path)
}
pub async fn read(&self) -> Result<Secret, Error> {
let mut buf = [0; SECRET_LEN];
inner_read(&self.0, &mut buf).await?;
Ok(Secret::new(buf))
}
pub async fn write(&self, secret: &Secret) -> Result<(), Error> {
inner_write(&self.0, secret.expose_secret()).await?;
Ok(())
}
}
impl Deref for SecretFile {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct AuthSecretFile(PathBuf);
impl AuthSecretFile {
pub fn new(path: PathBuf) -> Self {
Self(path)
}
pub async fn read(&self) -> Result<AuthSecret, Error> {
let mut buf = [0; AUTH_SECRET_LEN];
inner_read(&self.0, &mut buf).await?;
Ok(AuthSecret::new(buf))
}
pub async fn write(&self, secret: &AuthSecret) -> Result<(), Error> {
inner_write(&self.0, secret.expose_secret()).await?;
Ok(())
}
}
impl Deref for AuthSecretFile {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct NoiseFile(PathBuf);
impl NoiseFile {
pub fn new(path: PathBuf) -> Self {
Self(path)
}
pub async fn generate(&self) -> Result<(), Error> {
let path = self.0.clone();
task::spawn_blocking(|| {
use std::io::Write;
let mut file = std::fs::File::create(path)?;
#[cfg(unix)]
{
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
file.set_permissions(Permissions::from_mode(0o600))?;
}
let mut rng = thread_rng();
let mut buf = [0; 4096];
for _ in 0..500 {
rng.fill(&mut buf);
file.write_all(&buf)?;
}
file.sync_all()?;
Ok(())
})
.await
}
pub async fn read_secret(&self) -> Result<Secret, Error> {
let mut file = File::open(&self.0).await?;
let mut s = Strobe::new(b"DiscoHash", SecParam::B128);
let mut buf = [0; 4096];
for i in 0..500 {
file.read_exact(&mut buf).await?;
s.ad(&buf, i != 0);
}
let mut res = [0; SECRET_LEN];
s.prf(&mut res, false);
Ok(Secret::new(res))
}
pub async fn zeroize(&self) -> Result<(), Error> {
let mut file = OpenOptions::new().write(true).open(&self.0).await?;
for _ in 0..500 {
let buf = [0; 4096];
file.write_all(&buf).await?;
}
file.sync_all().await?;
Ok(())
}
}
impl Deref for NoiseFile {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[async_std::test]
async fn test_secret_file() {
let secret = Secret::generate().await;
let mut secret_file = std::env::temp_dir();
secret_file.push("secret_file");
let file = SecretFile::new(secret_file.into());
file.write(&secret).await.unwrap();
let secret2 = file.read().await.unwrap();
assert_eq!(secret.expose_secret(), secret2.expose_secret());
}
#[async_std::test]
async fn test_noise_file() {
let mut noise_file = std::env::temp_dir();
noise_file.push("noise_file");
let noise = NoiseFile::new(noise_file.into());
noise.generate().await.unwrap();
let n1 = noise.read_secret().await.unwrap();
let n2 = noise.read_secret().await.unwrap();
assert_eq!(n1.expose_secret(), n2.expose_secret());
noise.zeroize().await.unwrap();
let n2 = noise.read_secret().await.unwrap();
assert_ne!(n1.expose_secret(), n2.expose_secret());
}
}