wireguard_conf/utils/
keys.rs

1use core::fmt;
2
3use base64::prelude::*;
4use rand::RngCore;
5use x25519_dalek::{PublicKey as XPublicKey, StaticSecret};
6use zeroize::{Zeroize, ZeroizeOnDrop};
7
8use crate::WireguardError;
9
10/// Private key
11///
12/// Wrapper around [`x25519_dalek::StaticSecret`] with some traits.
13///
14/// # Implements
15///
16/// - Implements [`Zeroize`] and [`ZeroizeOnDrop`] for clearing secrets from memory.
17/// - Implements [`TryFrom<&str>`] or [`TryFrom<String>`] for importing key from Base64 format.
18/// - Implements [`fmt::Display`] for exporting key in Wireguard's format.
19/// - Implements [`fmt::Debug`].
20///
21/// # Examples
22///
23/// ```
24/// # use wireguard_conf::prelude::*;
25/// # fn main() -> WireguardResult<()> {
26/// // generate new random key:
27/// let key = PrivateKey::random();
28///
29/// // import key:
30/// let imported_key = PrivateKey::try_from("sJkP2oorqrq49P6Ln25MWo3X04PxhB8k+RnJJnZ4gEo=")?;
31///
32/// // export key via `fmt::Display` trait:
33/// let exported_key = imported_key.to_string();
34///
35/// assert_eq!(exported_key, "sJkP2oorqrq49P6Ln25MWo3X04PxhB8k+RnJJnZ4gEo=".to_string());
36/// # Ok(())
37/// # }
38/// ```
39#[derive(Clone, Zeroize, ZeroizeOnDrop)]
40pub struct PrivateKey(StaticSecret);
41
42impl PrivateKey {
43    /// Generate new a random [`PrivateKey`]
44    #[must_use]
45    pub fn random() -> PrivateKey {
46        Self(StaticSecret::random())
47    }
48}
49
50impl PrivateKey {
51    /// View private key as byte array.
52    #[inline]
53    #[must_use]
54    pub fn as_bytes(&self) -> &[u8; 32] {
55        self.0.as_bytes()
56    }
57
58    /// Convert private key to a byte array.
59    #[inline]
60    #[must_use]
61    pub fn to_bytes(&self) -> [u8; 32] {
62        self.0.to_bytes()
63    }
64}
65
66impl fmt::Debug for PrivateKey {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        f.debug_tuple("PrivateKey")
69            .field(&self.to_string())
70            .finish()
71    }
72}
73
74/// Export key as base64 for Wireguard.
75impl fmt::Display for PrivateKey {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        write!(f, "{}", BASE64_STANDARD.encode(self.as_bytes()))
78    }
79}
80
81impl PartialEq for PrivateKey {
82    fn eq(&self, other: &Self) -> bool {
83        self.as_bytes() == other.as_bytes()
84    }
85}
86
87impl TryFrom<&str> for PrivateKey {
88    type Error = WireguardError;
89
90    fn try_from(value: &str) -> Result<Self, Self::Error> {
91        let bytes: [u8; 32] = BASE64_STANDARD
92            .decode(value)
93            .map_err(|_| WireguardError::InvalidPrivateKey)?
94            .try_into()
95            .map_err(|_| WireguardError::InvalidPrivateKey)?;
96
97        Ok(Self(StaticSecret::from(bytes)))
98    }
99}
100
101impl TryFrom<String> for PrivateKey {
102    type Error = WireguardError;
103
104    fn try_from(value: String) -> Result<Self, Self::Error> {
105        Self::try_from(value.as_str())
106    }
107}
108
109impl From<[u8; 32]> for PrivateKey {
110    fn from(value: [u8; 32]) -> Self {
111        Self(StaticSecret::from(value))
112    }
113}
114
115/// Public key.
116///
117/// Wrapper around [`x25519_dalek::PublicKey`] with some traits.
118///
119/// # Implements
120///
121/// - Implements [`Zeroize`] and [`ZeroizeOnDrop`] for clearing secrets from memory.
122/// - Implements [`TryFrom<&str>`] or [`TryFrom<String>`] for importing key from Base64 format.
123/// - Implements [`From<&PrivateKey>`] for converting [`PrivateKey`] to [`PublicKey`].
124/// - Implements [`fmt::Display`] for exporting key in Wireguard's format.
125/// - Implements [`fmt::Debug`].
126///
127/// # Examples
128///
129/// ```
130/// # use wireguard_conf::prelude::*;
131/// # fn main() -> WireguardResult<()> {
132/// // generate new random key:
133/// let private_key = PrivateKey::random();         // 1. generate private key
134/// let public_key = PublicKey::from(&private_key); // 2. get public key via `From<&PrivateKey>`
135///
136/// // import key:
137/// let imported_key = PublicKey::try_from("ijxpP+2xo+s77bfbm4QZzl6OyYP7sIOTutqngQSlZBs=")?;
138///
139/// // export key via `fmt::Display` trait:
140/// let exported_key = imported_key.to_string();
141///
142/// assert_eq!(exported_key, "ijxpP+2xo+s77bfbm4QZzl6OyYP7sIOTutqngQSlZBs=".to_string());
143/// # Ok(())
144/// # }
145/// ```
146#[derive(Clone, PartialEq, Zeroize, ZeroizeOnDrop)]
147pub struct PublicKey(XPublicKey);
148
149impl PublicKey {
150    /// Convert this public key to a byte array.
151    #[inline]
152    #[must_use]
153    pub fn to_bytes(&self) -> [u8; 32] {
154        self.0.to_bytes()
155    }
156
157    /// View this public key as a byte array.
158    #[inline]
159    #[must_use]
160    pub fn as_bytes(&self) -> &[u8; 32] {
161        self.0.as_bytes()
162    }
163}
164
165impl fmt::Debug for PublicKey {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        f.debug_tuple("PublicKey").field(&self.to_string()).finish()
168    }
169}
170
171/// Export key in base64 format for Wireguard.
172impl fmt::Display for PublicKey {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        write!(f, "{}", BASE64_STANDARD.encode(self.as_bytes()))
175    }
176}
177
178impl TryFrom<&str> for PublicKey {
179    type Error = WireguardError;
180
181    fn try_from(value: &str) -> Result<Self, Self::Error> {
182        let bytes: [u8; 32] = BASE64_STANDARD
183            .decode(value)
184            .map_err(|_| WireguardError::InvalidPublicKey)?
185            .try_into()
186            .map_err(|_| WireguardError::InvalidPublicKey)?;
187
188        Ok(Self(XPublicKey::from(bytes)))
189    }
190}
191
192impl TryFrom<String> for PublicKey {
193    type Error = WireguardError;
194
195    fn try_from(value: String) -> Result<Self, Self::Error> {
196        Self::try_from(value.as_str())
197    }
198}
199
200impl From<[u8; 32]> for PublicKey {
201    fn from(value: [u8; 32]) -> Self {
202        Self(XPublicKey::from(value))
203    }
204}
205
206impl From<&PrivateKey> for PublicKey {
207    fn from(value: &PrivateKey) -> Self {
208        Self(XPublicKey::from(&value.0))
209    }
210}
211
212/// Preshared key.
213///
214/// Wrapper around `[u8; 32]`.
215///
216/// # Implements
217///
218/// - Implements [`Zeroize`] and [`ZeroizeOnDrop`] for clearing secrets from memory.
219/// - Implements [`TryFrom<&str>`] or [`TryFrom<String>`] for importing key from Base64 format.
220/// - Implements [`fmt::Display`] for exporting key in Wireguard's format.
221/// - Implements [`fmt::Debug`].
222///
223/// # Examples
224///
225/// ```
226/// # use wireguard_conf::prelude::*;
227/// # fn main() -> WireguardResult<()> {
228/// // generate new preshared key:
229/// let preshared_key = PresharedKey::random();
230///
231/// // import key:
232/// let imported_key = PresharedKey::try_from("MA4zR0tvQpZ4CQ7gs/KcAVMNMGIFBtDcfpjBr+0GwHY=")?;
233///
234/// // export key via `fmt::Display` trait:
235/// let exported_key = imported_key.to_string();
236///
237/// assert_eq!(exported_key, "MA4zR0tvQpZ4CQ7gs/KcAVMNMGIFBtDcfpjBr+0GwHY=".to_string());
238/// # Ok(())
239/// # }
240/// ```
241#[derive(Clone, PartialEq, Zeroize, ZeroizeOnDrop)]
242pub struct PresharedKey([u8; 32]);
243
244impl PresharedKey {
245    /// Generate random [`PresharedKey`].
246    #[must_use]
247    pub fn random() -> Self {
248        let mut key = [0u8; 32];
249        rand::rng().fill_bytes(&mut key);
250        Self(key)
251    }
252}
253
254impl PresharedKey {
255    /// Convert [`PresharedKey`] into byte array.
256    #[inline]
257    #[must_use]
258    pub fn to_bytes(&self) -> [u8; 32] {
259        self.0
260    }
261
262    /// Get [`PresharedKey`] as reference to bytes.
263    #[inline]
264    #[must_use]
265    pub fn as_bytes(&self) -> &[u8; 32] {
266        &self.0
267    }
268}
269
270impl fmt::Debug for PresharedKey {
271    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272        f.debug_tuple("PresharedKey")
273            .field(&self.to_string())
274            .finish()
275    }
276}
277
278/// Export [`PresharedKey`] as base64 format for Wireguard.
279impl fmt::Display for PresharedKey {
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        write!(f, "{}", BASE64_STANDARD.encode(self.as_bytes()))
282    }
283}
284
285impl From<[u8; 32]> for PresharedKey {
286    fn from(value: [u8; 32]) -> Self {
287        Self(value)
288    }
289}
290
291impl TryFrom<&str> for PresharedKey {
292    type Error = WireguardError;
293
294    fn try_from(value: &str) -> Result<Self, Self::Error> {
295        let bytes: [u8; 32] = BASE64_STANDARD
296            .decode(value)
297            .map_err(|_| WireguardError::InvalidPresharedKey)?
298            .try_into()
299            .map_err(|_| WireguardError::InvalidPresharedKey)?;
300
301        Ok(Self(bytes))
302    }
303}
304
305impl TryFrom<String> for PresharedKey {
306    type Error = WireguardError;
307
308    fn try_from(value: String) -> Result<Self, Self::Error> {
309        Self::try_from(value.as_str())
310    }
311}