Skip to content

[RFC,WIP] implement PQC PSK key exchange handshake#5

Draft
jonasjelonek wants to merge 10 commits intoopenwrt:pqcfrom
jonasjelonek:pqc
Draft

[RFC,WIP] implement PQC PSK key exchange handshake#5
jonasjelonek wants to merge 10 commits intoopenwrt:pqcfrom
jonasjelonek:pqc

Conversation

@jonasjelonek
Copy link
Copy Markdown

implements PQC PSK key exchange for unetd

Just as RFC for the handshake/key exchange process and how it's implemented.

This is still missing proper integration into unetd's internals, especially pex message send/recv flow.
This doesn't compile nor run yet.

nbd168 added 9 commits July 8, 2025 23:37
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Try getrandom/arc4random first, only fall back to /dev/urandom when necessary

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This will be used for quantum safe handshakes

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Used for ML-DSA

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Used for post-quantum signatures

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
@jonasjelonek jonasjelonek marked this pull request as draft July 25, 2025 09:53
Copy link
Copy Markdown
Member

@nbd168 nbd168 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good start! Added a few comments...

Comment thread wg-linux.c Outdated
Comment thread psk-kex.h
Comment thread host.c Outdated
Comment thread host.c Outdated
Comment thread CMakeLists.txt
Comment thread CMakeLists.txt Outdated
implements PQC PSK key exchange for unetd

THIS IS UNFINISHED AND DOES NOT WORK YET!

Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Copy link
Copy Markdown
Author

@jonasjelonek jonasjelonek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed all comments, split up messages, removed unnecessary (for now) changes

Comment thread psk-kex.c
return last == 0 || now - last > HANDSHAKE_INTERVAL;
}

/* This should be called regularly by some kind of timer */
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbd168 should this get its own timer or can we reuse one of the existing timers like request_update_timer or connect_timer?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should get its own timer.

Comment thread psk-kex.c
resp->last_handshake_time = peer->state.last_psk_handshake;
resp->need_handshake = psk_kex_need_handshake(peer);

pex_msg_send_ext(net, peer, /* address outside of tunnel */ addr);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbd168 how may I get the address to send this to the "peer" outside of the tunnel on global pex port?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should go through all addresses in peer->state.next_endpoint, possibly checking for duplicates.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my understanding:

  • what's the point of the different endpoint types?
  • why couldn't I just use peer->state.endpoint or peer->state.next_endpoint[ENDPOINT_TYPE_PEX] as destination?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ENDPOINT_TYPE_PEX is an endpoint address received from another host.
ENDPOINT_TYPE_ENDPOINT_NOTIFY and ENDPOINT_TYPE_ENDPOINT_PORT_NOTIFY are endpoint addresses received from peers directly (former from the PEX port, latter from the wireguard port).

If you don't have any peer connection yet, the ENDPOINT_TYPE_PEX will not be filled. Peers for other hosts are only exchanged over the tunnel.
As for the other types, availability depends on which packets make it through, especially in the NAT case. When the basic work on the handshake is done, we may need to add some improvements to NAT handling, since there will be some limitations compared to non-PEX handshake.

Comment thread psk-kex.c
default: return;
}

// TBD: do we need to explicitly trigger a network update to use the new PSK or does that work automatically?
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbd168 do you know?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do a wg_peer_update(net, peer, WG_PEER_UPDATE) call directly in order to upload the new psk for the peer to wireguard. Afterwards it should work on the next automatic handshake attempt.

Comment thread psk-kex.c
return last == 0 || now - last > HANDSHAKE_INTERVAL;
}

/* This should be called regularly by some kind of timer */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should get its own timer.

Comment thread psk-kex.c
if (peer->kex_ctx.role != PSK_KEX_ROLE_INITIATOR)
return;

/* TBD: Can we send two messages in a row without issues? */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend splitting the sending of messages from initializing the key exchange state. Messages may have to be retransmitted over unreliable networks, and it would be good to not have to redo the keygen part each time.

I also think it might be useful to encrypt the initial messages with a key derived from curve25519 DH, which can be calculated once per peer ahead of time. That reduces the DoS potential of expensive PQ crypto, as long as the non-PQ crypto has not been cracked yet.

Comment thread psk-kex.c
resp->last_handshake_time = peer->state.last_psk_handshake;
resp->need_handshake = psk_kex_need_handshake(peer);

pex_msg_send_ext(net, peer, /* address outside of tunnel */ addr);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should go through all addresses in peer->state.next_endpoint, possibly checking for duplicates.

Comment thread psk-kex.c
default: return;
}

// TBD: do we need to explicitly trigger a network update to use the new PSK or does that work automatically?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do a wg_peer_update(net, peer, WG_PEER_UPDATE) call directly in order to upload the new psk for the peer to wireguard. Afterwards it should work on the next automatic handshake attempt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants