Skip to content

Drop hardcoded 3, 4 major version literals in switch statements#7

Merged
qmuntal merged 2 commits into
mainfrom
dev/mclayton/case-default-cleanup
May 22, 2026
Merged

Drop hardcoded 3, 4 major version literals in switch statements#7
qmuntal merged 2 commits into
mainfrom
dev/mclayton/case-default-cleanup

Conversation

@michelle-clayton-work
Copy link
Copy Markdown
Contributor

Need to clean up leftover switch statements that hardcoded ossl 3 and 4 to fully support forward compatibility.

Convert:
switch major() {
case 1: ... legacy ...
case 3, 4: ... modern ...
default: panic(errUnsupportedVersion())
}

to:
switch major() {
case 1: ... legacy ...
default: ... modern ...
}

No behavior change for the currently tested majors. Future OpenSSL 5+ will exercise the 3+ code paths automatically once added to testedMajors in osslsetup/init.go.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates OpenSSL major-version dispatch logic to treat any non-1.x major as “modern” (3+), removing hardcoded case 3, 4 branches and associated unsupported-version panics to improve forward compatibility.

Changes:

  • Convert switch major() dispatch from case 3, 4 to default across multiple primitives so OpenSSL 5+ naturally follows the 3+ code paths.
  • Remove checkMajorVersion(3, 4) assertions inside 3+ helper functions and remove unreachable panic(errUnsupportedVersion()) branches.
  • Adjust a couple of version checks from switch major() to if major() != 1 for the “fetch for performance” paths.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tls1prf.go Routes non-1.x majors to the EVP_KDF (3+) TLS1-PRF implementation; removes 3/4-only guards.
rsa.go Treats non-1.x majors as “modern” RSA key generation/import paths; removes unsupported-version panics.
pbkdf2.go Routes non-1.x majors to EVP_KDF PBKDF2 fetch/derive paths; removes 3/4-only guards.
hmac.go Treats non-1.x majors as EVP_MAC(HMAC) path; removes unsupported-version panics.
hkdf.go Routes non-1.x majors to EVP_KDF HKDF/TLS13-KDF paths; removes 3/4-only guards.
evp.go Treats non-1.x majors as 3+ for EVP_MD/EVP_PKEY generation/provider logic and OAEP label handling.
ed25519.go Treats non-1.x majors as EVP_SIGNATURE-based Ed25519 support probing.
ecdsa.go Routes non-1.x majors to the param-builder (3+) ECDSA key path; removes 3/4-only guards.
ecdh.go Routes non-1.x majors to the param-builder (3+) ECDH key path; removes unsupported-version panics.
dsa.go Routes non-1.x majors to the param-builder (3+) DSA key path; removes 3/4-only guards.
cipher.go Treats non-1.x majors as 3+ for the “fetch once for performance” cipher caching path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 20, 2026

Benchmark Results

⚠️ Issues detected — expand failed jobs below for details

ubuntu-22.04-go1.25-cgo1-ossl3.0.13 · results
ubuntu-22.04-go1.25-cgo1-ossl3.1.5 · results
ubuntu-22.04-go1.25-cgo1-ossl3.2.1 · results
ubuntu-22.04-go1.25-cgo1-ossl3.3.1 · results
ubuntu-22.04-go1.25-cgo1-ossl3.4.0 · results
ubuntu-22.04-go1.25-cgo1-ossl3.5.0 · results
ubuntu-22.04-go1.26-cgo1-ossl3.0.13 · results
ubuntu-22.04-go1.26-cgo1-ossl3.1.5 · results
ubuntu-22.04-go1.26-cgo1-ossl3.2.1 · results
ubuntu-22.04-go1.26-cgo1-ossl3.3.1 · results
ubuntu-22.04-go1.26-cgo1-ossl3.4.0 · results
ubuntu-22.04-go1.26-cgo1-ossl3.5.0 · results
ubuntu-24.04-arm-go1.25-cgo1-ossl3.0.13 · results
ubuntu-24.04-arm-go1.25-cgo1-ossl3.1.5 · results
ubuntu-24.04-arm-go1.25-cgo1-ossl3.2.1 · results

ubuntu-24.04-arm-go1.25-cgo1-ossl3.3.1

Regressions:

alloc regression: PBKDF2HMACSHA1-4 [B/op] +0.32% (p=0.041)

📁 Full results

ubuntu-24.04-arm-go1.25-cgo1-ossl3.4.0 · results

ubuntu-24.04-arm-go1.25-cgo1-ossl3.5.0

Regressions:

alloc regression: MLKEMRoundTrip/Alice-4 [B/op] +0.07% (p=0.020)

📁 Full results

ubuntu-24.04-arm-go1.26-cgo1-ossl3.0.13 · results
ubuntu-24.04-arm-go1.26-cgo1-ossl3.1.5 · results
ubuntu-24.04-arm-go1.26-cgo1-ossl3.2.1 · results
ubuntu-24.04-arm-go1.26-cgo1-ossl3.3.1 · results
ubuntu-24.04-arm-go1.26-cgo1-ossl3.4.0 · results
ubuntu-24.04-arm-go1.26-cgo1-ossl3.5.0 · results
ubuntu-latest-go1.25-cgo1-azl3 · results
ubuntu-latest-go1.26-cgo1-azl3 · results

@michelle-clayton-work michelle-clayton-work changed the title Convert switch major() dispatch to default + drop checkMajorVersion(3… Drop hardcoded 3, 4 major version literals in switch statements May 21, 2026
@michelle-clayton-work
Copy link
Copy Markdown
Contributor Author

@qmuntal do you have a strategy in mind to tackle this switch getOSSLDigetsContext? It's a little more complex so I wasn't sure if it was as easy to just remove it

@qmuntal
Copy link
Copy Markdown
Member

qmuntal commented May 21, 2026

@qmuntal do you have a strategy in mind to tackle this switch getOSSLDigetsContext? It's a little more complex so I wasn't sure if it was as easy to just remove it

We should mark the hash objects as not marshalable when not using a known version.

Copy link
Copy Markdown
Member

@qmuntal qmuntal left a comment

Choose a reason for hiding this comment

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

checkMajorVersion shouldn't be used anymore, no? Can it be removed?

Per ADR 0018, OpenSSL 3+ guarantees ABI/API compatibility within a major
version, and the osslsetup startup check rejects untested majors (with a
GODEBUG=ms_opensslallowuntested=1 escape hatch). Code that runs on
OpenSSL 3+ should not need to enumerate concrete majors; `case 3, 4:`
was forcing every new major to touch every file.

Convert `switch major() { case 1: ...; case 3, 4: ...; default:
panic(errUnsupportedVersion()) }` patterns to `case 1: ...; default:
...`. For single-case `switch major() { case 3, 4: ... }` patterns that
no-op on 1, switch to `if major() != 1`.

Also remove `checkMajorVersion`: every call sat inside a function only
reached from the matching arm of the outer dispatch, so the assertion
was always redundant. Update versionguardcheck to drop it from the
gated-call set.

In getOSSLDigetsContext, replace `panic(errUnsupportedVersion())` in
the default arm with `return nil`. Callers already surface nil as
errHashStateInvalid; degrading to an error is safer than crashing when
the escape hatch surfaces a major whose EVP_MD_CTX layout we don't
know.

No behavior change for the currently tested majors. Future OpenSSL 5+
will exercise the 3+ code paths automatically once whitelisted in
osslsetup/init.go testedMajors.
Hash.MarshalBinary/UnmarshalBinary parses the running EVP_MD_CTX state
by overlaying a Go struct whose layout matches OpenSSL's evp_md_ctx_st
for the current major. When the loaded major is one we have not tested
(only reachable behind GODEBUG=ms_opensslallowuntested=1), that overlay
would read unknown memory.

Per qmuntal's review on #7, mark such hashes as not marshallable so
MarshalBinary/UnmarshalBinary short-circuit with the existing
errMarshallUnsupported{} (which unwraps to errors.ErrUnsupported)
instead of risking a wrong-layout access:

  * osslsetup: add IsTestedMajor(m int) bool, exposing the existing
    testedMajors gate that openLibrary uses at startup.
  * openssl: add knownMajor() wrapper for the same check.
  * loadHash: AND the per-provider marshallable assignment with
    knownMajor() so untested-major hashes report not marshallable.
  * getOSSLDigetsContext: update the default-arm comment now that
    the upstream gate makes the path effectively unreachable; the
    nil return becomes defense in depth.
@michelle-clayton-work michelle-clayton-work force-pushed the dev/mclayton/case-default-cleanup branch from e9cecb8 to 031ad9f Compare May 21, 2026 23:42
@michelle-clayton-work
Copy link
Copy Markdown
Contributor Author

@qmuntal do you have a strategy in mind to tackle this switch getOSSLDigetsContext? It's a little more complex so I wasn't sure if it was as easy to just remove it

We should mark the hash objects as not marshalable when not using a known version.

Let me know if this makes sense -- added some testedMajors wrapper, then gated hash.marshallable on it in loadHash and MarshalBinary/UnmarshalBinary now short-circuit with the existing errMarshallUnsupported{} instead of touching unknown EVP_MD_CTX memory

Copy link
Copy Markdown
Member

@qmuntal qmuntal left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@qmuntal qmuntal merged commit 5eb8c49 into main May 22, 2026
112 of 115 checks passed
@qmuntal qmuntal deleted the dev/mclayton/case-default-cleanup branch May 22, 2026 07:29
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