Skip to main content

wireguard_conf/keys/
public.rs

1use core::fmt;
2
3use base64::prelude::*;
4use x25519_dalek::PublicKey as XPublicKey;
5use zeroize::{Zeroize, ZeroizeOnDrop};
6
7use crate::WireguardError;
8
9use super::PrivateKey;
10
11/// Public key.
12///
13/// Wrapper around [`x25519_dalek::PublicKey`] with some traits.
14///
15/// # Implements
16///
17/// - Implements [`Zeroize`] and [`ZeroizeOnDrop`] for clearing secrets from memory.
18/// - Implements [`TryFrom<&str>`] or [`TryFrom<String>`] for importing key from Base64 format.
19/// - Implements [`From<&PrivateKey>`] for converting [`PrivateKey`] to [`PublicKey`].
20/// - Implements [`fmt::Display`] for exporting key in Wireguard's format.
21/// - Implements [`fmt::Debug`].
22///
23/// # Examples
24///
25/// ```
26/// # use wireguard_conf::prelude::*;
27/// # fn main() -> WireguardResult<()> {
28/// // generate new random key:
29/// let private_key = PrivateKey::random();         // 1. generate private key
30/// let public_key = PublicKey::from(&private_key); // 2. get public key via `From<&PrivateKey>`
31///
32/// // import key:
33/// let imported_key = PublicKey::try_from("ijxpP+2xo+s77bfbm4QZzl6OyYP7sIOTutqngQSlZBs=")?;
34///
35/// // export key via `fmt::Display` trait:
36/// let exported_key = imported_key.to_string();
37///
38/// assert_eq!(exported_key, "ijxpP+2xo+s77bfbm4QZzl6OyYP7sIOTutqngQSlZBs=".to_string());
39/// # Ok(())
40/// # }
41/// ```
42#[derive(Clone, PartialEq, Zeroize, ZeroizeOnDrop)]
43pub struct PublicKey(XPublicKey);
44
45impl PublicKey {
46    /// Convert this public key to a byte array.
47    #[inline]
48    #[must_use]
49    pub fn to_bytes(&self) -> [u8; 32] {
50        self.0.to_bytes()
51    }
52
53    /// View this public key as a byte array.
54    #[inline]
55    #[must_use]
56    pub fn as_bytes(&self) -> &[u8; 32] {
57        self.0.as_bytes()
58    }
59}
60
61impl fmt::Debug for PublicKey {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        f.debug_tuple("PublicKey").field(&self.to_string()).finish()
64    }
65}
66
67/// Export key in base64 format for Wireguard.
68impl fmt::Display for PublicKey {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "{}", BASE64_STANDARD.encode(self.as_bytes()))
71    }
72}
73
74impl TryFrom<&str> for PublicKey {
75    type Error = WireguardError;
76
77    fn try_from(value: &str) -> Result<Self, Self::Error> {
78        let bytes: [u8; 32] = BASE64_STANDARD
79            .decode(value)
80            .map_err(|_| WireguardError::InvalidPublicKey)?
81            .try_into()
82            .map_err(|_| WireguardError::InvalidPublicKey)?;
83
84        Ok(Self(XPublicKey::from(bytes)))
85    }
86}
87
88impl TryFrom<String> for PublicKey {
89    type Error = WireguardError;
90
91    fn try_from(value: String) -> Result<Self, Self::Error> {
92        Self::try_from(value.as_str())
93    }
94}
95
96impl From<[u8; 32]> for PublicKey {
97    fn from(value: [u8; 32]) -> Self {
98        Self(XPublicKey::from(value))
99    }
100}
101
102impl From<&PrivateKey> for PublicKey {
103    fn from(value: &PrivateKey) -> Self {
104        Self(XPublicKey::from(&value.0))
105    }
106}
107
108#[cfg(feature = "serde")]
109mod serde_impl {
110    use super::PublicKey;
111    use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
112
113    impl Serialize for PublicKey {
114        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115        where
116            S: Serializer,
117        {
118            if serializer.is_human_readable() {
119                serializer.serialize_str(&self.to_string())
120            } else {
121                serializer.serialize_bytes(self.as_bytes())
122            }
123        }
124    }
125
126    impl<'de> Deserialize<'de> for PublicKey {
127        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128        where
129            D: Deserializer<'de>,
130        {
131            if deserializer.is_human_readable() {
132                let data = String::deserialize(deserializer)?;
133
134                PublicKey::try_from(data.as_str()).map_err(|_| {
135                    de::Error::invalid_value(de::Unexpected::Str(&data), &"a public key")
136                })
137            } else {
138                let bytes = <[u8; 32]>::deserialize(deserializer)?;
139
140                Ok(PublicKey::from(bytes))
141            }
142        }
143    }
144}