Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@
**Vulnerability:** Intermediate key material (`secret` extracted from chaining key, and expanded `t*_plus` buffers) in `src/wireguard/crypto.zig`'s HKDF implementation (`kdf1`, `kdf2`, `kdf3`) was left on the stack without being zeroed. These buffers contain sensitive pseudorandom keys derived from the handshake chaining key.
**Learning:** Helper functions often create temporary buffers on the stack which persist until overwritten. Unlike the main handshake state struct, these are ephemeral but critical. Zig does not automatically zero stack variables on return.
**Prevention:** Always use `defer std.crypto.secureZero(u8, &var);` immediately after declaring any variable that will hold sensitive key material, especially in crypto primitives.

## 2026-10-25 - [CRITICAL] Timing Attack on Static Public Key Comparison
**Vulnerability:** In `src/wireguard/noise.zig`, the `consumeInitiation` and `consumeInitiationFast` functions used `std.mem.eql` to verify the decrypted initiator's static public key against the expected remote peer's static public key. `std.mem.eql` terminates early on the first mismatched byte, exposing a timing oracle. An attacker could potentially use this side-channel to forge identities or glean information about the expected public keys within the Noise protocol state machine.
**Learning:** Even though public keys are ostensibly public information, within the context of a cryptographic protocol state machine where they form the basis for establishing trust, identity matching must be treated as a cryptographic check requiring constant-time execution to prevent information leakage.
**Prevention:** Always use `std.crypto.timing_safe.eql` (specifically with the `[32]u8` type signature in Zig 0.15) when comparing public keys and other cryptographic identities for authorization or verification inside the Noise handshake.
6 changes: 4 additions & 2 deletions src/wireguard/noise.zig
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ pub const Handshake = struct {
crypto.mixHash(&hash, &msg.encrypted_static);

// Verify the decrypted static matches what we expect
if (!std.mem.eql(u8, &initiator_static, &self.remote_static)) {
// SECURITY: Must use constant-time comparison to prevent timing attacks on public key identity validation.
if (!std.crypto.timing_safe.eql([32]u8, initiator_static, self.remote_static)) {
return error.UnknownPeer;
}

Expand Down Expand Up @@ -282,7 +283,8 @@ pub const Handshake = struct {
var hash = preamble.hash;

// Verify the decrypted static matches what we expect
if (!std.mem.eql(u8, &preamble.initiator_static, &self.remote_static)) {
// SECURITY: Must use constant-time comparison to prevent timing attacks on public key identity validation.
if (!std.crypto.timing_safe.eql([32]u8, preamble.initiator_static, self.remote_static)) {
return error.UnknownPeer;
}

Expand Down