ouisync/crypto/
password.rs1use 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#[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
27impl 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
66impl 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);