Skip to content

xtables-wgobfs: add an iptables extension module to obfuscate WireGuard#19974

Open
infinet wants to merge 1 commit intoopenwrt:masterfrom
infinet:xtables-wgobfs
Open

xtables-wgobfs: add an iptables extension module to obfuscate WireGuard#19974
infinet wants to merge 1 commit intoopenwrt:masterfrom
infinet:xtables-wgobfs

Conversation

@infinet
Copy link

@infinet infinet commented Nov 27, 2022

Maintainer: @infinet
Compile tested: (ramips-mt7620 19.07.9 and 22.03.2, x86_64 19.07.9)
Run tested: (x86_64 19.07.9)

Port from https://github.com/infinet/xt_wgobfs, this extension obfuscates WireGuard by:

  • The first 16 bytes of WG message is obfuscated.

  • The mac2 field is also obfuscated, if it is all zeros.

  • Padding WG message with random long random bytes.

  • Drop keepalive message with 80% probability.

  • Change the Diffserv field to zero.

See https://github.com/infinet/xt_wgobfs/blob/main/README.md for usage.

Example as client:

Add two iptables rules to firewall.user:

iptables -t mangle -I INPUT -p udp -m udp --sport 6789 -j WGOBFS --key mysecretkey --unobfs

iptables -t mangle -I OUTPUT -p udp -m udp --dport 6789 -j WGOBFS --key mysecretkey --obfs

Here assuming remote server is listening on port 6789.

Signed-off-by: Wei Chen weichen302@gmail.com

@infinet infinet changed the title add an iptables extension module to obfuscate WireGuard xtables-wgobfs: add an iptables extension module to obfuscate WireGuard Nov 27, 2022
@infinet
Copy link
Author

infinet commented Nov 29, 2022

I changed the tile. Could you please re-run the Formalities test? Thanks!

@Ansuel
Copy link
Member

Ansuel commented Nov 29, 2022

You need to change commit title also

@github-actions
Copy link

No usage of AUTORELEASE found in changes

@jefferyto
Copy link
Member

Not sure why but only 1 of the usual 10+ checks ran, I suggest rebasing on current master and doing a force push.

@valga
Copy link

valga commented Aug 27, 2023

@infinet It seems that your ChaCha8 hash function implementation has some crypto related flaws compared to reference ChaCha20 one:

  1. Although your macro called EIGHT_ROUNDS it runs only 4 rounds, so your hash is actually ChaCha4 - https://github.com/infinet/xt_wgobfs/blob/440a2ff505fec95a393c4a316ae2811c60ae0948/src/chacha8.c#L76-L79 - you have to call DOUBLE_ROUND 4 times to get 8 rounds, see reference https://git.zx2c4.com/wireguard-linux-compat/tree/src/crypto/zinc/chacha20/chacha20.c#n71
  2. You use ChaCha state part related to key in return value, whereas ChaCha20 skips it to nonce one - https://git.zx2c4.com/wireguard-linux-compat/tree/src/crypto/zinc/chacha20/chacha20.c#n150
  3. ChaCha state after rounds gets ORed with the initial state - https://github.com/infinet/xt_wgobfs/blob/440a2ff505fec95a393c4a316ae2811c60ae0948/src/chacha8.c#L101 - considering previous point it gets ORed with 4 well known constants and first half of the key. ChaCha20 hash doesn`t do OR before return at all.

@infinet
Copy link
Author

infinet commented Aug 28, 2023

Thank you for the insight! It will take some time to review and correct that part of code. In general, this module is trying to remove wireguard packets signature with minimal overhead, while wireguard is taking care of the communication security.

@infinet
Copy link
Author

infinet commented Sep 4, 2023

I've taken another look at chacha8 implementation.

  1. You're correct; the chacha8 indeed only does 4 rounds. Need to test the performance of 8 rounds. If the overhead is similar to current 4 rounds, then revise the code to 8 rounds.

  2. Wireguard uses XChaCha variant. It uses 128 bits nonce and original 256 bits key to generate a derived key, which is used by chacha20 with 96 bits nonce to encrypt message. The derived key generation takes first 32 bits and last 32 bits(Edit: first and last 128 bits) of multiple quarter rounds output, as you mentioned in remark 2.

  3. The keystream used by wireguard to encrypt message is generated by ChaCha20 block function. Its output is different from the key derive function. It adds the original state to the quarter rounds output. This module use the same approach.
    https://git.zx2c4.com/wireguard-linux-compat/tree/src/crypto/zinc/chacha20/chacha20.c#n84

@valga
Copy link

valga commented Sep 4, 2023

@infinet I also reviewed everything again and found this useful piece of information - https://crypto.stackexchange.com/a/11215

In short, original state addition (feed-forward) is required when you take parts of result that is being added with key (64 bits in the middle). In other words, you can omit feed-forward if you take first and last 32 bits, because they are being added with known input, so it will improve performance a bit.

So the only concern is number of rounds.

@infinet
Copy link
Author

infinet commented Sep 18, 2023

@infinet I also reviewed everything again and found this useful piece of information - https://crypto.stackexchange.com/a/11215

So the only concern is number of rounds.

I have made few modifications.

  • The hash function is now chacha6 instead of chacha4.
  • Input to chacha is 16 bytes instead of 8 bytes.
  • All inputs for chacha are from the 16th to 31st bytes of WG message. First byte is used as counter and incremented each time to generate a new PRN.

The performance is almost the same.

@infinet
Copy link
Author

infinet commented Mar 22, 2024

I have rebased it on the latest master branch.

@GeorgeSapkin
Copy link
Member

@infinet still relevant? If so commits need to be formatted according to the submission guidelines. And since this is adding a new package, this can be just one commit.

@infinet
Copy link
Author

infinet commented Dec 13, 2025

Yes, it’s still relevant. I have updated the PR and squashed them into a single commit. Thanks!

@GeorgeSapkin
Copy link
Member

I think the commit subject should be something like:

xtables-wgobfs: add new package

And the rest go into the commit message.

@GeorgeSapkin
Copy link
Member

You need to address the formality check failures.

@infinet
Copy link
Author

infinet commented Dec 13, 2025

Thanks! I have made two changes. Do they look reasonable?

@infinet infinet force-pushed the xtables-wgobfs branch 2 times, most recently from 36c628c to 1c88ad6 Compare December 13, 2025 17:18
@infinet
Copy link
Author

infinet commented Dec 15, 2025

Hopefully I have fixed all the formalities error in CI checks.

Port from https://github.com/infinet/xt_wgobfs, this kernel module
obfuscates WireGuard. It can work as server, client, or relay.

Performance
===========

Test in two Alpine linux VMs on same host. Each VM has 1 CPU and 256M RAM.
Iperf3 over wg reports 1.1Gbits/sec without obfuscation, 950Mbits/sec with
obfuscation.

How it works
============

The sender and receiver share a secret key, which is used by `chacha6` to
hash the same input into identical pseudo-random numbers. These
pseudo-random numbers are used in obfuscation. The input to hash function
is from 16th to 31st bytes of a WG message. The first byte of input is
incremented when need to generate a different PRN.

- The first 16 bytes of WG message is obfuscated.

- The mac2 field is also obfuscated, if it is all zeros.

- Padding WG message with random bytes of random length.

- Drop keepalive message with 80% probability.

- Change the Diffserv field to zero.

`Chacha6` is chosen for its speed, as the goal is not encryption.

See https://github.com/infinet/xt_wgobfs/blob/main/README.md for usage.

Signed-off-by: Wei Chen <weichen302@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants