ouisync/crypto/
password.rs

1use argon2::password_hash;
2use ouisync_macros::api;
3use rand::{rngs::OsRng, Rng};
4use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
5use std::{array::TryFromSliceError, fmt, sync::Arc};
6use subtle::ConstantTimeEq;
7use zeroize::Zeroizing;
8
9/// A simple wrapper over String to avoid certain kinds of attack. For more elaboration please see
10/// the documentation for the SecretKey structure.
11#[derive(Clone)]
12#[api(repr(String), secret)]
13pub struct Password(Arc<Zeroizing<String>>);
14
15impl From<String> for Password {
16    fn from(pwd: String) -> Self {
17        Self(Arc::new(Zeroizing::new(pwd)))
18    }
19}
20
21impl AsRef<str> for Password {
22    fn as_ref(&self) -> &str {
23        self.0.as_ref()
24    }
25}
26
27/// Note this impl uses constant-time operations (using [subtle](https://crates.io/crates/subtle))
28/// and so provides protection against software side-channel attacks.
29impl PartialEq for Password {
30    fn eq(&self, other: &Self) -> bool {
31        self.0.as_bytes().ct_eq(other.0.as_bytes()).into()
32    }
33}
34
35impl Eq for Password {}
36
37impl fmt::Debug for Password {
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        write!(f, "****")
40    }
41}
42
43impl Serialize for Password {
44    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
45    where
46        S: Serializer,
47    {
48        self.as_ref().serialize(s)
49    }
50}
51
52impl<'de> Deserialize<'de> for Password {
53    fn deserialize<D>(d: D) -> Result<Self, D::Error>
54    where
55        D: Deserializer<'de>,
56    {
57        Ok(Self::from(String::deserialize(d)?))
58    }
59}
60
61#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
62#[repr(transparent)]
63#[api(repr(Bytes))]
64pub struct PasswordSalt([u8; Self::SIZE]);
65
66// NOTE: Not using the `define_byte_array_wrapper` macro here because this type needs to be visible
67// to the API parser which is currently not smart emough to see inside macros.
68// TODO: consider changing `define_byte_array_wrapper` to a proc-macro
69
70impl PasswordSalt {
71    pub const SIZE: usize = password_hash::Salt::RECOMMENDED_LENGTH;
72
73    pub fn as_array(&self) -> &[u8; Self::SIZE] {
74        &self.0
75    }
76
77    pub fn random() -> Self {
78        OsRng.r#gen()
79    }
80}
81
82impl From<[u8; Self::SIZE]> for PasswordSalt {
83    fn from(array: [u8; Self::SIZE]) -> Self {
84        Self(array)
85    }
86}
87
88impl TryFrom<&'_ [u8]> for PasswordSalt {
89    type Error = TryFromSliceError;
90
91    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
92        Ok(Self(slice.try_into()?))
93    }
94}
95
96impl AsRef<[u8]> for PasswordSalt {
97    fn as_ref(&self) -> &[u8] {
98        &self.0[..]
99    }
100}
101
102impl fmt::Debug for PasswordSalt {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        write!(f, "{:<8x}", self)
105    }
106}
107
108impl fmt::LowerHex for PasswordSalt {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        hex_fmt::HexFmt(&self.0).fmt(f)
111    }
112}
113
114impl Serialize for PasswordSalt {
115    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
116    where
117        S: Serializer,
118    {
119        serde_bytes::Bytes::new(self.as_ref()).serialize(s)
120    }
121}
122
123impl<'de> Deserialize<'de> for PasswordSalt {
124    fn deserialize<D>(d: D) -> Result<Self, D::Error>
125    where
126        D: Deserializer<'de>,
127    {
128        let bytes: &serde_bytes::Bytes = Deserialize::deserialize(d)?;
129
130        if bytes.len() != Self::SIZE {
131            return Err(D::Error::invalid_length(
132                bytes.len(),
133                &format!("{}", Self::SIZE).as_str(),
134            ));
135        }
136
137        let mut salt = [0; Self::SIZE];
138        salt.as_mut().copy_from_slice(bytes);
139
140        Ok(PasswordSalt(salt))
141    }
142}
143
144derive_rand_for_wrapper!(PasswordSalt);