tld-registration/src/password.rs

64 lines
1.9 KiB
Rust

use serde::de::Visitor;
use serde::{Deserialize, Serialize};
use sodiumoxide::crypto::pwhash::argon2id13::{self, HashedPassword};
use sodiumoxide::utils::memzero;
#[derive(Serialize, PartialEq, Eq)]
pub struct Password(String);
impl Password {
pub fn hashed(&self) -> Result<HashedPassword, ()> {
argon2id13::pwhash(
self.0.as_bytes(),
argon2id13::OPSLIMIT_INTERACTIVE,
argon2id13::MEMLIMIT_INTERACTIVE,
)
}
pub fn verify(&self, password: &HashedPassword) -> bool {
argon2id13::pwhash_verify(password, self.0.as_bytes())
}
}
/// A custom drop implementation is necessary for this type as we need to ensure
/// that the password is not stored in memory for an extended period of time.
impl Drop for Password {
fn drop(&mut self) {
// SAFETY: self.0 is never accessed after calling drop.
memzero(unsafe { self.0.as_bytes_mut() })
}
}
impl<'de> Deserialize<'de> for Password {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{Error, Unexpected};
struct SecretDeserializer;
impl<'de> Visitor<'de> for SecretDeserializer {
type Value = Password;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a password between 8 and 64 bytes")
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
if v.len() < 8 || v.len() > 64 {
println!("password failed");
return Err(Error::invalid_value(
Unexpected::Str("password with invalid size"),
&"a password between 8 and 64 bytes",
));
}
Ok(Password(v.to_owned()))
}
}
deserializer.deserialize_string(SecretDeserializer)
}
}