Skip to content

Use FIPS-safe default curve (P-256) instead of X25519#817

Merged
JoTurk merged 3 commits intopion:masterfrom
samthesloth:fix-fips-default-curve
Mar 30, 2026
Merged

Use FIPS-safe default curve (P-256) instead of X25519#817
JoTurk merged 3 commits intopion:masterfrom
samthesloth:fix-fips-default-curve

Conversation

@samthesloth
Copy link
Copy Markdown
Contributor

Description

On FIPS 140-enabled Linux systems, DTLS handshakes fail because the initial curve is hardcoded to X25519, which Go 1.24+ rejects at runtime (crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode).

Reference issue

Fixes #816

Changes

  1. conn.go — Changed defaultNamedCurve from X25519 to P256
  2. config.go — Reordered defaultCurves to {P256, P384, X25519} (P-256 first, X25519 kept for non-FIPS compatibility)
  3. flight0handler.go / flight1handler.go — Changed state.namedCurve = defaultNamedCurve to state.namedCurve = cfg.ellipticCurves[0] so the flight handlers respect the caller's configured curves instead of using a hardcoded constant

Testing

I ran into 3 issues when running golangci-lint run referring to file formatting pointing at the comments at the top of the files (authentication_type.go, key_exchange_algorithm.go, and util.go) but they were unmodified files, so I wasn't sure if I should touch them.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 24, 2026

Codecov Report

❌ Patch coverage is 16.66667% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.49%. Comparing base (848c4bc) to head (5ed8021).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
conn.go 0.00% 5 Missing and 1 partial ⚠️
flight0handler.go 33.33% 1 Missing and 1 partial ⚠️
flight1handler.go 33.33% 1 Missing and 1 partial ⚠️

❌ Your patch status has failed because the patch coverage (16.66%) is below the target coverage (70.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #817      +/-   ##
==========================================
- Coverage   82.64%   82.49%   -0.15%     
==========================================
  Files         121      121              
  Lines        6858     6868      +10     
==========================================
- Hits         5668     5666       -2     
- Misses        779      787       +8     
- Partials      411      415       +4     
Flag Coverage Δ
go 82.49% <16.66%> (-0.15%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@samthesloth samthesloth force-pushed the fix-fips-default-curve branch from 158daa0 to 0e14a77 Compare March 24, 2026 19:43
Copy link
Copy Markdown
Member

@theodorsm theodorsm left a comment

Choose a reason for hiding this comment

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

Hey, Thanks for discovering this issue and fixing it so fast! Compliance to FIPS is imporant to do right and I'm sure others will be happy for this to be fixed.

I have left some comments on the changing of prefered default order.

Comment thread config.go Outdated
const defaultMTU = 1200 // bytes

var defaultCurves = []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384} //nolint:gochecknoglobals
var defaultCurves = []elliptic.Curve{elliptic.P256, elliptic.P384, elliptic.X25519} //nolint:gochecknoglobals
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.

We should not change this order, this is the standard followed by other DTLS-implementations such as NSS (firefox), boringssl (chrome). OpenSSL also seem to have X25519 prefered by default. This might affect interopt and allow the library to be fingerprintable more easily.

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.

@theodorsm @samthesloth Okay what if we only switch if fips is enabled? https://pkg.go.dev/crypto/fips140#Enabled i think that would be a good compromise.

Comment thread conn.go Outdated
cookieLength = 20
sessionLength = 32
defaultNamedCurve = elliptic.X25519
defaultNamedCurve = elliptic.P256
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 guess we could remove this const as it's not needed anymore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@theodorsm Maybe we use a func instead that goes through the list, skipping X25519 if FIPS is enabled as @JoTurk suggested?

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 agree, that sounds like the way to go!

@samthesloth
Copy link
Copy Markdown
Contributor Author

@theodorsm Hello again! Sorry if I'm doing this wrong. I think I implemented a proper change, but please let me know if I need to change how I went about it or how I pushed it! Thank you!

Copy link
Copy Markdown
Member

@theodorsm theodorsm left a comment

Choose a reason for hiding this comment

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

@samthesloth, no worries! Thinking a bit more about it, we should move the FIPS check as early as possible. Left some comments on how we can approach this.

Comment thread conn.go Outdated
Comment on lines +1354 to +1361
if fips140.Enabled() {
// On FIPS systems, skip non-approved curves
for _, c := range curves {
if c != elliptic.X25519 {
return c
}
}
}
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.

Let's move this check to just after we set the curves, and drop the function:

dtls/conn.go

Lines 178 to 181 in d0e736c

curves := config.EllipticCurves
if len(curves) == 0 {
curves = defaultCurves
}

We are using cfg.ellipticCurves in other flights to set the SupportedEllipticCurves extension, ex:

dtls/flight3handler.go

Lines 297 to 301 in d0e736c

if state.namedCurve != 0 {
extensions = append(extensions, []extension.Extension{
&extension.SupportedEllipticCurves{
EllipticCurves: cfg.ellipticCurves,
},

So if we filter earlier, we avoid announcing non-FIPS curves in the extension also.

Comment thread flight0handler.go Outdated
state.localEpoch.Store(zeroEpoch)
state.remoteEpoch.Store(zeroEpoch)
state.namedCurve = defaultNamedCurve
state.namedCurve = defaultCurve(cfg.ellipticCurves)
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.

After moving the check in conn.go we should check for length here:

if len(cfg.ellipticCurves) < 1 {
 return nil,nil,errEmptyEllipticCurves
}
state.namedCurve = cfg.ellipticCurves[0]

Comment thread flight1handler.go Outdated
state.localEpoch.Store(zeroEpoch)
state.remoteEpoch.Store(zeroEpoch)
state.namedCurve = defaultNamedCurve
state.namedCurve = defaultCurve(cfg.ellipticCurves)
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.

Also check length here if we move check in conn.go.

@theodorsm
Copy link
Copy Markdown
Member

@samthesloth, this looks really good now! Could you also re-try what you reported in your inital issue #816 to verify that this fixes your use case?

@samthesloth
Copy link
Copy Markdown
Contributor Author

Hello! Sorry for the late reply. I was having some issues recreating everything. I tested on Rocky Linux 9 with Go 1.26.1. I confirmed that crypto/ecdh.X25519() is blocked under GODEBUG=fips140=only while P256() works, so the fix is correct. I wasn't able to do a full end-to-end test in that mode because fips140=only also blocks SHA-1, which the WebSocket protocol requires for connection handshakes, so my program panics before reaching the DTLS layer.

The exact partial-enforcement scenario from my original report (X25519 blocked, SHA-1 allowed) may have been triggered by an earlier Go build. However, the flight handler change to use cfg.ellipticCurves[0] is the right fix as you suggested since it respects the caller's config instead of hardcoding X25519. Happy to add a test if helpful.

Copy link
Copy Markdown
Member

@JoTurk JoTurk left a comment

Choose a reason for hiding this comment

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

thanks.

@theodorsm theodorsm self-requested a review March 30, 2026 14:26
Copy link
Copy Markdown
Member

@theodorsm theodorsm left a comment

Choose a reason for hiding this comment

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

@samthesloth thanks! You can add a E2E test for FIPS if you want to, but I don't think it's strictly needed for this small and reasonable fix. Could be useful for later if we want to verify FIPS compliance. Can also be added later in another PR if you want to work on it:)

I see that this branch now is behind the master branch, so please rebase - then we can squash and merge.

@samthesloth samthesloth force-pushed the fix-fips-default-curve branch from 10ba769 to 5ed8021 Compare March 30, 2026 14:49
@samthesloth
Copy link
Copy Markdown
Contributor Author

@theodorsm Awesome, thank you! I'd love to! For now, I think I rebased correctly? Let me know if I need to change anything!

@JoTurk JoTurk merged commit e6950f0 into pion:master Mar 30, 2026
17 of 19 checks passed
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.

DTLS handshake fails on FIPS-enabled Linux: defaultNamedCurve hardcoded to X25519

3 participants