wireguard_conf/utils/
keys.rs

1use core::fmt;
2
3use base64::prelude::*;
4use x25519_dalek::{PublicKey as XPublicKey, StaticSecret};
5use zeroize::{Zeroize, ZeroizeOnDrop};
6
7use crate::WireguardError;
8
9/// Private key
10///
11/// Wrapper around [`x25519_dalek::StaticSecret`] with some traits.
12///
13/// # Implements
14///
15/// - Implements [`Zeroize`] and [`ZeroizeOnDrop`] for clearing secrets from memory.
16/// - Implements [`TryFrom<&str>`] or [`TryFrom<String>`] for importing key from Base64 format.
17/// - Implements [`fmt::Display`] for exporting key in Wireguard's format.
18/// - Implements [`fmt::Debug`].
19///
20/// # Examples
21///
22/// ```
23/// # use wireguard_conf::prelude::*;
24/// # fn main() -> WireguardResult<()> {
25/// // generate new random key:
26/// let key = PrivateKey::random();
27///
28/// // import key:
29/// let imported_key = PrivateKey::try_from("sJkP2oorqrq49P6Ln25MWo3X04PxhB8k+RnJJnZ4gEo=")?;
30///
31/// // export key via `fmt::Display` trait:
32/// let exported_key = imported_key.to_string();
33///
34/// assert_eq!(exported_key, "sJkP2oorqrq49P6Ln25MWo3X04PxhB8k+RnJJnZ4gEo=".to_string());
35/// # Ok(())
36/// # }
37/// ```
38#[derive(Clone, Zeroize, ZeroizeOnDrop)]
39pub struct PrivateKey(StaticSecret);
40
41impl PrivateKey {
42    /// Generate new a random [`PrivateKey`]
43    #[must_use]
44    pub fn random() -> PrivateKey {
45        Self(StaticSecret::random())
46    }
47}
48
49impl PrivateKey {
50    /// View private key as byte array.
51    #[inline]
52    #[must_use]
53    pub fn as_bytes(&self) -> &[u8; 32] {
54        self.0.as_bytes()
55    }
56
57    /// Convert private key to a byte array.
58    #[inline]
59    #[must_use]
60    pub fn to_bytes(&self) -> [u8; 32] {
61        self.0.to_bytes()
62    }
63}
64
65impl fmt::Debug for PrivateKey {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        f.debug_tuple("PrivateKey")
68            .field(&self.to_string())
69            .finish()
70    }
71}
72
73/// Export key as base64 for Wireguard.
74impl fmt::Display for PrivateKey {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "{}", BASE64_STANDARD.encode(self.as_bytes()))
77    }
78}
79
80impl PartialEq for PrivateKey {
81    fn eq(&self, other: &Self) -> bool {
82        self.as_bytes() == other.as_bytes()
83    }
84}
85
86impl TryFrom<&str> for PrivateKey {
87    type Error = WireguardError;
88
89    fn try_from(value: &str) -> Result<Self, Self::Error> {
90        let bytes: [u8; 32] = BASE64_STANDARD
91            .decode(value)
92            .map_err(|_| WireguardError::InvalidPrivateKey)?
93            .try_into()
94            .map_err(|_| WireguardError::InvalidPrivateKey)?;
95
96        Ok(Self(StaticSecret::from(bytes)))
97    }
98}
99
100impl TryFrom<String> for PrivateKey {
101    type Error = WireguardError;
102
103    fn try_from(value: String) -> Result<Self, Self::Error> {
104        Self::try_from(value.as_str())
105    }
106}
107
108/// Public key.
109///
110/// Wrapper around [`x25519_dalek::PublicKey`] with some traits.
111///
112/// # Implements
113///
114/// - Implements [`Zeroize`] and [`ZeroizeOnDrop`] for clearing secrets from memory.
115/// - Implements [`TryFrom<&str>`] or [`TryFrom<String>`] for importing key from Base64 format.
116/// - Implements [`From<&PrivateKey>`] for converting [`PrivateKey`] to [`PublicKey`].
117/// - Implements [`fmt::Display`] for exporting key in Wireguard's format.
118/// - Implements [`fmt::Debug`].
119///
120/// # Examples
121///
122/// ```
123/// # use wireguard_conf::prelude::*;
124/// # fn main() -> WireguardResult<()> {
125/// // generate new random key:
126/// let private_key = PrivateKey::random();         // 1. generate private key
127/// let public_key = PublicKey::from(&private_key); // 2. get public key via `From<&PrivateKey>`
128///
129/// // import key:
130/// let imported_key = PublicKey::try_from("ijxpP+2xo+s77bfbm4QZzl6OyYP7sIOTutqngQSlZBs=")?;
131///
132/// // export key via `fmt::Display` trait:
133/// let exported_key = imported_key.to_string();
134///
135/// assert_eq!(exported_key, "ijxpP+2xo+s77bfbm4QZzl6OyYP7sIOTutqngQSlZBs=".to_string());
136/// # Ok(())
137/// # }
138/// ```
139#[derive(Clone, PartialEq, Zeroize, ZeroizeOnDrop)]
140pub struct PublicKey(XPublicKey);
141
142impl PublicKey {
143    /// Convert this public key to a byte array.
144    #[inline]
145    #[must_use]
146    pub fn to_bytes(&self) -> [u8; 32] {
147        self.0.to_bytes()
148    }
149
150    /// View this public key as a byte array.
151    #[inline]
152    #[must_use]
153    pub fn as_bytes(&self) -> &[u8; 32] {
154        self.0.as_bytes()
155    }
156}
157
158impl fmt::Debug for PublicKey {
159    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160        f.debug_tuple("PublicKey").field(&self.to_string()).finish()
161    }
162}
163
164/// Export key in base64 format for Wireguard.
165impl fmt::Display for PublicKey {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        write!(f, "{}", BASE64_STANDARD.encode(self.as_bytes()))
168    }
169}
170
171impl TryFrom<&str> for PublicKey {
172    type Error = WireguardError;
173
174    fn try_from(value: &str) -> Result<Self, Self::Error> {
175        let bytes: [u8; 32] = BASE64_STANDARD
176            .decode(value)
177            .map_err(|_| WireguardError::InvalidPublicKey)?
178            .try_into()
179            .map_err(|_| WireguardError::InvalidPublicKey)?;
180
181        Ok(Self(XPublicKey::from(bytes)))
182    }
183}
184
185impl TryFrom<String> for PublicKey {
186    type Error = WireguardError;
187
188    fn try_from(value: String) -> Result<Self, Self::Error> {
189        Self::try_from(value.as_str())
190    }
191}
192
193impl From<&PrivateKey> for PublicKey {
194    fn from(value: &PrivateKey) -> Self {
195        Self(XPublicKey::from(&value.0))
196    }
197}