Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Breaking changes

- [Rewritten how AmneziaWG values are handled](./MIGRATION.md).
- [Rewritten how AmneziaWG values are handled](./docs/MIGRATION.md).

Now `amneziawg` feature has both versions of AmneziaWG.

Expand Down
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Wireguard Conf
# wireguard-conf

[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/idkncc/wireguard-conf/build.yml)](https://github.com/idkncc/wireguard-conf/actions)
[![docs.rs](https://img.shields.io/docsrs/wireguard-conf)](https://docs.rs/wireguard-conf)
[![Crates.io Version](https://img.shields.io/crates/v/wireguard-conf)](https://crates.io/crates/wireguard-conf)
[![Crates.io Total Downloads](https://img.shields.io/crates/d/wireguard-conf)](https://crates.io/crates/wireguard-conf)
[![Crates.io MSRV](https://img.shields.io/crates/msrv/wireguard-conf)](https://crates.io/crates/wireguard-conf)
[![Quickstart Tutorial](https://img.shields.io/badge/Quickstart_Tutorial-blue)](./QUICKSTART.md)
[![Quickstart Tutorial](https://img.shields.io/badge/Quickstart_Tutorial-blue)](./docs/QUICKSTART.md)

Easy to use library for creating wireguard configs.

Expand All @@ -19,7 +19,7 @@ cargo add wireguard-conf

### Usage

The best way to understand library: [Quickstart](./QUICKSTART.md)!
The best way to understand library: [Quickstart](./docs/QUICKSTART.md)!

More usage examples you can find in [tests/tutorial.rs](tests/tutorial.rs), [tests folder](tests/) and documentation examples on [docs.rs](https://docs.rs/wireguard-conf).

Expand Down Expand Up @@ -47,19 +47,16 @@ println!("{}", peer.to_interface(&interface, ToInterfaceOptions::new()).unwrap()

### Features

- `amneziawg-*`: AmneziaWG is a fork of wireguard, that less recognizable by DPI systems by
randomizing packet headers with packet sizes.
- `amneziawg`: adds support for generating and manually building amnezia settings via
[`AmneziaWG`](https://docs.rs/wireguard-conf/latest/wireguard-conf/enum.AmneziaWG.html) enum.

These feature flags add support for generating and manually building amnezia settings:

- `amneziawg`: _alias for `amneziawg-1`, this will update in next major release_
- `amneziawg-1`: adds [AmneziaWG 1.0](https://docs.amnezia.org/documentation/amnezia-wg/) support.
- `amneziawg-2`: adds [AmneziaWG 2.0](https://docs.amnezia.org/documentation/amnezia-wg/) support. **(recommended over `amneziawg-1`)**
- ~~`amneziawg-3`~~: **WIP** (waiting for official release)
> **AmneziaWG** is a fork of wireguard, that less recognizable by DPI systems by
> randomizing packet headers with packet sizes.

- `serde`: adds implementions of `serde::Serialize` and `serde::Deserialize` for all structs.

## Misc

- [Contributing](./CONTRIBUTING.md)
- [Major releases migration guides](./docs/MIGRATION.md)
- Licensed under either [MIT](./LICENSE-MIT) or [Apache 2.0](./LICENSE-APACHE)
File renamed without changes.
File renamed without changes.
11 changes: 6 additions & 5 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ build:

alias doc := docs
docs:
cargo doc {{CFLAGS}}
docs-open:
cargo doc {{CFLAGS}} --open
cargo doc --all-features {{CFLAGS}}

docs-rs:
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --no-deps --all-features {{CFLAGS}}

fmt:
cargo fmt {{CFLAGS}}
lint:
cargo clippy --all-features {{CFLAGS}}
cargo clippy --all-targets --all-features {{CFLAGS}}
lint-fix:
cargo clippy --all-features --fix --allow-dirty {{CFLAGS}}
cargo clippy --all-targets --all-features --fix --allow-dirty {{CFLAGS}}
1 change: 1 addition & 0 deletions src/amneziawg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::WireguardResult;
/// ```
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum AmneziaWG {
/// AmneziaWG 1.0
V1(AmneziaWG1),
Expand Down
12 changes: 12 additions & 0 deletions src/amneziawg/ver1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ macro_rules! assert_return {

/// **AmneziaWG 1.0** obfuscation values.
///
/// If you need to generate, build, validate or use these settings in interface, **use [`AmneziaWG`]
/// enum instead**.
///
/// ```
/// # use wireguard_conf::prelude::*;
/// let settings = AmneziaWG::random_v1();
///
/// _ = Interface::builder()
/// .amnezia_settings(settings)
/// .build();
/// ```
///
/// - [Documentation](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module?tab=readme-ov-file#configuration)
#[must_use]
#[derive(Clone, Debug, PartialEq)]
Expand Down
17 changes: 17 additions & 0 deletions src/amneziawg/ver2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ impl fmt::Display for HRange {

/// **AmneziaWG 2.0** obfuscation values.
///
/// If you need to generate, build, validate or use these settings in interface, **use [`AmneziaWG`]
/// enum instead**.
///
/// ```
/// # use wireguard_conf::prelude::*;
/// let settings = AmneziaWG::random_v2();
/// let settings = AmneziaWG::builder_v2()
/// .jc(9)
/// // <snip>
/// .build()
/// .expect("invalid settings");
///
/// _ = Interface::builder()
/// .amnezia_settings(settings)
/// .build();
/// ```
///
/// - [Documentation](https://docs.amnezia.org/documentation/amnezia-wg)
#[must_use]
#[derive(Clone, Debug, PartialEq, Default, Builder)]
Expand Down
33 changes: 33 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use thiserror::Error;

/// `wireguard-conf` error.
#[derive(Error, Debug, PartialEq)]
pub enum WireguardError {
/// Error, when private key is invalid.
#[error("invalid private key")]
InvalidPrivateKey,

/// Error, when public key is invalid.
#[error("invalid public key")]
InvalidPublicKey,

/// Error, when preshared key is invalid.
#[error("invalid preshared key")]
InvalidPresharedKey,

/// Error, when private key isn't provided.
#[error("no private key provided")]
NoPrivateKeyProvided,

/// Error, when no IP assigned.
#[error("no assigned ip")]
NoAssignedIP,

/// Error, when some amnezia setting is invalid
#[cfg(feature = "amneziawg")]
#[error("invalid amnezia setting: {0}")]
InvalidAmneziaSetting(String),
}

/// Result alias.
pub type WireguardResult<T> = Result<T, WireguardError>;
7 changes: 7 additions & 0 deletions src/keys/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod preshared;
mod private;
mod public;

pub use preshared::PresharedKey;
pub use private::PrivateKey;
pub use public::PublicKey;
146 changes: 146 additions & 0 deletions src/keys/preshared.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use core::fmt;

use base64::prelude::*;
use rand::Rng;
use zeroize::{Zeroize, ZeroizeOnDrop};

use crate::WireguardError;

/// Preshared key.
///
/// Wrapper around `[u8; 32]`.
///
/// # Implements
///
/// - Implements [`Zeroize`] and [`ZeroizeOnDrop`] for clearing secrets from memory.
/// - Implements [`TryFrom<&str>`] or [`TryFrom<String>`] for importing key from Base64 format.
/// - Implements [`fmt::Display`] for exporting key in Wireguard's format.
/// - Implements [`fmt::Debug`].
///
/// # Examples
///
/// ```
/// # use wireguard_conf::prelude::*;
/// # fn main() -> WireguardResult<()> {
/// // generate new preshared key:
/// let preshared_key = PresharedKey::random();
///
/// // import key:
/// let imported_key = PresharedKey::try_from("MA4zR0tvQpZ4CQ7gs/KcAVMNMGIFBtDcfpjBr+0GwHY=")?;
///
/// // export key via `fmt::Display` trait:
/// let exported_key = imported_key.to_string();
///
/// assert_eq!(exported_key, "MA4zR0tvQpZ4CQ7gs/KcAVMNMGIFBtDcfpjBr+0GwHY=".to_string());
/// # Ok(())
/// # }
/// ```
#[derive(Clone, PartialEq, Zeroize, ZeroizeOnDrop)]
pub struct PresharedKey([u8; 32]);

impl PresharedKey {
/// Generate random [`PresharedKey`].
#[must_use]
pub fn random() -> Self {
let mut key = [0u8; 32];
rand::rng().fill_bytes(&mut key);
Self(key)
}
}

impl PresharedKey {
/// Convert [`PresharedKey`] into byte array.
#[inline]
#[must_use]
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}

/// Get [`PresharedKey`] as reference to bytes.
#[inline]
#[must_use]
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}

impl fmt::Debug for PresharedKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("PresharedKey")
.field(&self.to_string())
.finish()
}
}

/// Export [`PresharedKey`] as base64 format for Wireguard.
impl fmt::Display for PresharedKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", BASE64_STANDARD.encode(self.as_bytes()))
}
}

impl From<[u8; 32]> for PresharedKey {
fn from(value: [u8; 32]) -> Self {
Self(value)
}
}

impl TryFrom<&str> for PresharedKey {
type Error = WireguardError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
let bytes: [u8; 32] = BASE64_STANDARD
.decode(value)
.map_err(|_| WireguardError::InvalidPresharedKey)?
.try_into()
.map_err(|_| WireguardError::InvalidPresharedKey)?;

Ok(Self(bytes))
}
}

impl TryFrom<String> for PresharedKey {
type Error = WireguardError;

fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_from(value.as_str())
}
}

#[cfg(feature = "serde")]
mod serde_impl {
use super::PresharedKey;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};

impl Serialize for PresharedKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_string())
} else {
serializer.serialize_bytes(self.as_bytes())
}
}
}

impl<'de> Deserialize<'de> for PresharedKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let data = String::deserialize(deserializer)?;

PresharedKey::try_from(data.as_str()).map_err(|_| {
de::Error::invalid_value(de::Unexpected::Str(&data), &"a preshared key")
})
} else {
let bytes = <[u8; 32]>::deserialize(deserializer)?;

Ok(PresharedKey::from(bytes))
}
}
}
}
Loading