wireguard_control/
key.rs

1use std::{ffi::NulError, fmt};
2
3use x25519_dalek::{PublicKey, StaticSecret};
4
5/// Represents an error in base64 key parsing.
6#[derive(Eq, PartialEq, Debug, Clone)]
7pub struct InvalidKey;
8
9impl std::error::Error for InvalidKey {}
10
11impl fmt::Display for InvalidKey {
12    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13        write!(f, "Invalid key format")
14    }
15}
16
17impl From<NulError> for InvalidKey {
18    fn from(_: NulError) -> Self {
19        InvalidKey {}
20    }
21}
22
23/// Represents a WireGuard encryption key.
24///
25/// WireGuard makes no meaningful distinction between public,
26/// private and preshared keys - any sequence of 32 bytes
27/// can be used as either of those.
28///
29/// This means that you need to be careful when working with
30/// `Key`s, especially ones created from external data.
31#[derive(PartialEq, Eq, Clone, Hash)]
32pub struct Key(pub [u8; 32]);
33
34impl Key {
35    /// Generates and returns a new private key.
36    pub fn generate_private() -> Self {
37        use rand_core::{OsRng, RngCore};
38
39        let mut bytes = [0u8; 32];
40        OsRng.fill_bytes(&mut bytes);
41
42        // Apply key clamping.
43        bytes[0] &= 248;
44        bytes[31] &= 127;
45        bytes[31] |= 64;
46        Self(bytes)
47    }
48
49    /// Generates and returns a new preshared key.
50    #[must_use]
51    pub fn generate_preshared() -> Self {
52        use rand_core::{OsRng, RngCore};
53
54        let mut key = [0u8; 32];
55        OsRng.fill_bytes(&mut key);
56        Self(key)
57    }
58
59    /// Generates a public key for this private key.
60    #[must_use]
61    pub fn get_public(&self) -> Self {
62        let secret = StaticSecret::from(self.0);
63        let public = PublicKey::from(&secret);
64
65        Self(public.to_bytes())
66    }
67
68    /// Generates an all-zero key.
69    #[must_use]
70    pub fn zero() -> Self {
71        Self([0u8; 32])
72    }
73
74    pub fn as_bytes(&self) -> &[u8] {
75        &self.0
76    }
77
78    /// Converts the key to a standardized base64 representation, as used by the `wg` utility and `wg-quick`.
79    pub fn to_base64(&self) -> String {
80        base64::encode(self.0)
81    }
82
83    /// Converts a base64 representation of the key to the raw bytes.
84    ///
85    /// This can fail, as not all text input is valid base64 - in this case
86    /// `Err(InvalidKey)` is returned.
87    pub fn from_base64(key: &str) -> Result<Self, crate::InvalidKey> {
88        let mut key_bytes = [0u8; 32];
89        let decoded_bytes = base64::decode(key).map_err(|_| InvalidKey)?;
90
91        if decoded_bytes.len() != 32 {
92            return Err(InvalidKey);
93        }
94
95        key_bytes.copy_from_slice(&decoded_bytes[..]);
96        Ok(Self(key_bytes))
97    }
98
99    pub fn from_hex(hex_str: &str) -> Result<Self, crate::InvalidKey> {
100        let mut sized_bytes = [0u8; 32];
101        hex::decode_to_slice(hex_str, &mut sized_bytes).map_err(|_| InvalidKey)?;
102        Ok(Self(sized_bytes))
103    }
104}
105
106#[cfg(test)]
107mod test {
108    use super::*;
109
110    #[test]
111    fn test_pubkey_generation() {
112        let privkey = "SGb+ojrRNDuMePufwtIYhXzA//k6wF3R21tEBgKlzlM=";
113        let pubkey = "DD5yKRfzExcV5+kDnTroDgCU15latdMjiQ59j1hEuk8=";
114
115        let private = Key::from_base64(privkey).unwrap();
116        let public = Key::get_public(&private);
117
118        assert_eq!(public.to_base64(), pubkey);
119    }
120
121    #[test]
122    fn test_rng_sanity_private() {
123        let first = Key::generate_private();
124        assert!(first.as_bytes() != [0u8; 32]);
125        for _ in 0..100_000 {
126            let key = Key::generate_private();
127            assert!(first != key);
128            assert!(key.as_bytes() != [0u8; 32]);
129        }
130    }
131
132    #[test]
133    fn test_rng_sanity_preshared() {
134        let first = Key::generate_preshared();
135        assert!(first.as_bytes() != [0u8; 32]);
136        for _ in 0..100_000 {
137            let key = Key::generate_preshared();
138            assert!(first != key);
139            assert!(key.as_bytes() != [0u8; 32]);
140        }
141    }
142}
143
144/// Represents a pair of private and public keys.
145///
146/// This struct is here for convenience of generating
147/// a complete keypair, e.g. for a new peer.
148#[derive(Debug, PartialEq, Eq, Clone)]
149pub struct KeyPair {
150    /// The private key.
151    pub private: Key,
152    /// The public key.
153    pub public: Key,
154}
155
156impl fmt::Debug for Key {
157    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158        write!(f, "Key(\"{}\")", self.to_base64())
159    }
160}
161
162impl KeyPair {
163    pub fn generate() -> Self {
164        let private = Key::generate_private();
165        let public = private.get_public();
166        KeyPair { private, public }
167    }
168
169    pub fn from_private(key: Key) -> Self {
170        let public = key.get_public();
171        KeyPair {
172            private: key,
173            public,
174        }
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    #[test]
181    fn test_key_zero() {
182        use crate::key::Key;
183
184        let key = Key::generate_preshared();
185        assert_ne!(key.as_bytes(), &[0u8; 32]);
186    }
187
188    #[test]
189    fn test_key_base64() {
190        use crate::key::Key;
191
192        let key = Key::generate_preshared();
193        let key_b64 = key.to_base64();
194        let key_new = Key::from_base64(&key_b64).unwrap();
195
196        assert_eq!(key, key_new);
197    }
198
199    #[test]
200    fn test_invalid_key() {
201        use crate::key::{InvalidKey, Key};
202
203        let key_b64: String = Key::generate_preshared()
204            .to_base64()
205            .chars()
206            .rev()
207            .collect();
208
209        assert_eq!(Key::from_base64(&key_b64), Err(InvalidKey));
210    }
211
212    #[test]
213    fn test_generate_keypair_basic() {
214        use crate::key::Key;
215
216        let privkey = Key::generate_private();
217        let pubkey = privkey.get_public();
218
219        assert_ne!(privkey, pubkey);
220    }
221
222    #[test]
223    fn test_generate_keypair_helper() {
224        use crate::key::KeyPair;
225        let pair = KeyPair::generate();
226
227        assert_ne!(pair.private, pair.public);
228    }
229}