comments inline

On 1/16/26 4:33 PM, Christoph Heiss wrote:

[snip]

> +impl PrivateKey {
> +    /// Length of the raw private key data in bytes.
> +    pub const RAW_LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
> +
> +    /// Generates a new private key suitable for use with WireGuard.
> +    pub fn generate() -> Result<Self, Error> {
> +        generate_key().map(Self)
> +    }
> +
> +    /// Calculates the public key from the private key.
> +    pub fn public_key(&self) -> PublicKey {
> +        PublicKey(
> +            ed25519_dalek::SigningKey::from_bytes(&self.0)
> +                .verifying_key()
> +                .to_bytes(),
> +        )
> +    }
> +
> +    /// Returns the raw private key material.
> +    #[must_use]
> +    pub fn raw(&self) -> &ed25519_dalek::SecretKey {
> +        &self.0
> +    }

might be better to implement AsRef instead for ergonomics?

> +    /// Builds a new [`PrivateKey`] from raw key material.
> +    #[must_use]
> +    pub fn from_raw(data: ed25519_dalek::SecretKey) -> Self {
> +        // [`SigningKey`] takes care of correct key clamping.
> +        Self(SigningKey::from(&data).to_bytes())
> +    }
> +}
> +
> +impl From<ed25519_dalek::SecretKey> for PrivateKey {
> +    fn from(value: ed25519_dalek::SecretKey) -> Self {
> +        Self(value)
> +    }
> +}
> +
> +/// Preshared key between two WireGuard peers.
> +#[derive(Clone, Deserialize, Serialize)]
> +#[serde(transparent)]
> +pub struct PresharedKey(
> +    #[serde(with = "proxmox_serde::byte_array_as_base64")] 
> ed25519_dalek::SecretKey,
> +);
> +
> +impl fmt::Debug for PresharedKey {
> +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
> +        write!(f, "<preshared-key>")
> +    }
> +}
> +
> +impl PresharedKey {
> +    /// Length of the raw private key data in bytes.
> +    pub const RAW_LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
> +
> +    /// Generates a new preshared key suitable for use with WireGuard.
> +    pub fn generate() -> Result<Self, Error> {
> +        generate_key().map(Self)
> +    }
> +
> +    /// Returns the raw private key material.
> +    #[must_use]
> +    pub fn raw(&self) -> &ed25519_dalek::SecretKey {
> +        &self.0
> +    }
> +

here too: might be better to implement AsRef instead for ergonomics?

> +    /// Builds a new [`PrivateKey`] from raw key material.
> +    #[must_use]
> +    pub fn from_raw(data: ed25519_dalek::SecretKey) -> Self {
> +        // [`SigningKey`] takes care of correct key clamping.
> +        Self(SigningKey::from(&data).to_bytes())
> +    }
> +}
> +
> +/// A single WireGuard peer.
> +#[derive(Serialize, Debug)]
> +#[serde(rename_all = "PascalCase")]
> +pub struct WireGuardPeer {
> +    /// Public key, matching the private key of of the remote peer.
> +    pub public_key: PublicKey,
> +    /// Additional key preshared between two peers. Adds an additional layer 
> of symmetric-key
> +    /// cryptography to be mixed into the already existing public-key 
> cryptography, for
> +    /// post-quantum resistance.
> +    pub preshared_key: PresharedKey,
> +    /// List of IPv4/v6 CIDRs from which incoming traffic for this peer is 
> allowed and to which
> +    /// outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 
> may be specified for
> +    /// matching all IPv4 addresses, and ::/0 may be specified for matching 
> all IPv6 addresses.
> +    #[serde(rename = "AllowedIPs")]
> +    pub allowed_ips: Vec<Cidr>,

potentially missing skip_serializing_if = "Vec::is_empty"?

[snip]

> +#[cfg(test)]
> +mod tests {
> +    use std::net::Ipv4Addr;
> +
> +    use proxmox_network_types::ip_address::Cidr;
> +
> +    use crate::{PresharedKey, PrivateKey, WireGuardConfig, 
> WireGuardInterface, WireGuardPeer};
> +
> +    fn mock_private_key(v: u8) -> PrivateKey {
> +        let base = v * 32;
> +        PrivateKey((base..base + 
> 32).collect::<Vec<u8>>().try_into().unwrap())
> +    }
> +
> +    fn mock_preshared_key(v: u8) -> PresharedKey {
> +        let base = v * 32;
> +        PresharedKey((base..base + 
> 32).collect::<Vec<u8>>().try_into().unwrap())
> +    }
> +
> +    #[test]
> +    fn single_peer() {
> +        let config = WireGuardConfig {
> +            interface: WireGuardInterface {
> +                private_key: mock_private_key(0),
> +                listen_port: Some(51820),
> +                fw_mark: Some(127),
> +            },
> +            peers: vec![WireGuardPeer {
> +                public_key: mock_private_key(1).public_key(),
> +                preshared_key: mock_preshared_key(1),
> +                allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 0, 
> 0), 24).unwrap()],
> +                endpoint: Some("foo.example.com:51820".parse().unwrap()),
> +                persistent_keepalive: Some(25),
> +            }],
> +        };
> +
> +        pretty_assertions::assert_eq!(
> +            config.to_raw_config().unwrap(),
> +            "[Interface]
> +PrivateKey = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=
> +ListenPort = 51820
> +FwMark = 127
> +
> +[Peer]
> +PublicKey = Kay64UG8yvCyLhqU000LxzYeUm0L/hLIl5S8kyKWbdc=
> +PresharedKey = ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=
> +AllowedIPs = 192.168.0.0/24
> +Endpoint = foo.example.com:51820
> +PersistentKeepalive = 25
> +"
> +        );
> +    }
> +
> +    #[test]
> +    fn multiple_peers() {
> +        let config = WireGuardConfig {
> +            interface: WireGuardInterface {
> +                private_key: mock_private_key(0),
> +                listen_port: Some(51820),
> +                fw_mark: None,
> +            },
> +            peers: vec![
> +                WireGuardPeer {
> +                    public_key: mock_private_key(1).public_key(),
> +                    preshared_key: mock_preshared_key(1),
> +                    allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 
> 0, 0), 24).unwrap()],
> +                    endpoint: Some("foo.example.com:51820".parse().unwrap()),
> +                    persistent_keepalive: None,
> +                },
> +                WireGuardPeer {
> +                    public_key: mock_private_key(2).public_key(),
> +                    preshared_key: mock_preshared_key(2),
> +                    allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 
> 1, 0), 24).unwrap()],
> +                    endpoint: None,
> +                    persistent_keepalive: Some(25),
> +                },
> +                WireGuardPeer {
> +                    public_key: mock_private_key(3).public_key(),
> +                    preshared_key: mock_preshared_key(3),
> +                    allowed_ips: vec![Cidr::new_v4(Ipv4Addr::new(192, 168, 
> 2, 0), 24).unwrap()],
> +                    endpoint: None,
> +                    persistent_keepalive: None,
> +                },
> +            ],
> +        };
> +
> +        pretty_assertions::assert_eq!(
> +            config.to_raw_config().unwrap(),
> +            "[Interface]
> +PrivateKey = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=
> +ListenPort = 51820
> +
> +[Peer]
> +PublicKey = Kay64UG8yvCyLhqU000LxzYeUm0L/hLIl5S8kyKWbdc=
> +PresharedKey = ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=
> +AllowedIPs = 192.168.0.0/24
> +Endpoint = foo.example.com:51820
> +
> +[Peer]
> +PublicKey = JUO5L/EJVRFHatyDadtt3JM2ZaEZeN2hQE7hBmypVZ0=
> +PresharedKey = QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl8=
> +AllowedIPs = 192.168.1.0/24
> +PersistentKeepalive = 25
> +
> +[Peer]
> +PublicKey = F0VTtFbd38aQjsqxwQH+arIeK6oGF3lbfUOmNIKZP9U=
> +PresharedKey = YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=
> +AllowedIPs = 192.168.2.0/24
> +"
> +        );
> +    }
> +}

maybe add some test cases for missing properties as well (or adapt the
existing ones)?



Reply via email to