diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000000..8f9b7fdf563 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,240 @@ +# crypto-backend + +Use when implementing, modifying, or adding crypto algorithms to the Microsoft build of Go crypto backend system. Covers the end-to-end workflow: editing Go source in the submodule, backend architecture patterns, and extracting changes into patches with git go-patch. + +## Development workflow + +See [eng/doc/DeveloperGuide.md](/eng/doc/DeveloperGuide.md) for the full development workflow, including how to set up the repository, build, test, and work with `git go-patch`. + +## Architecture overview + +The Microsoft build of Go replaces upstream BoringCrypto (`crypto/internal/boring`) with a pluggable backend system (`crypto/internal/backend`). The backend dispatches crypto operations to platform-native libraries: + +- **Linux**: OpenSSL via `github.com/microsoft/go-crypto-openssl` +- **Windows**: CNG via `github.com/microsoft/go-crypto-winnative` +- **macOS**: CryptoKit/CommonCrypto via `github.com/microsoft/go-crypto-darwin` + +The backend is gated by the `goexperiment.systemcrypto` build tag. When disabled, `nobackend.go` provides panic stubs so the code compiles but never executes. + +## Build tag conventions + +- All backend files use `//go:build goexperiment.systemcrypto`. The `nobackend.go` uses `//go:build !goexperiment.systemcrypto`. +- Prefer filename suffixes (`_linux.go`, `_windows.go`, `_darwin.go`) over build tag OS constraints. + - **Do**: `foo_linux.go` with `//go:build goexperiment.systemcrypto` + - **Don't**: `foo.go` with `//go:build goexperiment.systemcrypto && linux` +- The `boring.go` / `notboring.go` file pair in crypto packages uses `goexperiment.systemcrypto` / `!goexperiment.systemcrypto` build tags respectively. +- Legacy build tags (`goexperiment.opensslcrypto`, `goexperiment.cngcrypto`, `goexperiment.darwincrypto`) are still emitted for source compatibility but must not be used in new code. + +## Import alias convention + +Always import the backend package as `boring`: + +```go +import ( + boring "crypto/internal/backend" + "crypto/internal/backend/bbig" +) +``` + +This preserves compatibility with upstream BoringCrypto code paths and the `bcache` caching infrastructure. + +## How to add a new crypto algorithm + +### 1. Add backend shims (all four files) + +For each platform backend file, add: +- A `Supports*()` function that delegates to the platform library +- Type aliases for key types +- Shim functions for each operation + +```go +// backend_linux.go +func SupportsMLKEM768() bool { return openssl.SupportsMLKEM768() } +type DecapsulationKeyMLKEM768 = openssl.DecapsulationKeyMLKEM768 +func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { + return openssl.GenerateKeyMLKEM768() +} +``` + +For `nobackend.go`, add matching panic stubs: + +```go +func SupportsMLKEM768() bool { panic("cryptobackend: not available") } +type DecapsulationKeyMLKEM768 struct{} +func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { + panic("cryptobackend: not available") +} +``` + +**nobackend.go stub rules**: +- Functions: panic with `"cryptobackend: not available"` +- Types: empty structs (`struct{}` or `struct{ _ int }`) +- Methods on stub types: panic +- `Supports*()`: must panic (not return false) — they should never be called when `Enabled` is `false` +- Exception: `SupportsChaCha20Poly1305()` returns `false` (for FIPS-only mode checks) + +If an operation is not supported on a platform (e.g., Darwin doesn't support DSA), the platform backend should also panic with `"cryptobackend: not available"` and the `Supports*` function should return `false`. + +### 2. Integrate into the crypto package + +Add a backend key field to the key struct. **Be conscious of allocations** — avoid unnecessary pointer indirection when the backend type can be stored by value, but note that some backend pointers' lifetimes may be tied to system-provided objects, so this must be a deliberate decision per type: + +```go +// Prefer value types when the backend type allows it +type DecapsulationKey768 struct { + key *mlkem.DecapsulationKey768 + boringKey boring.DecapsulationKeyMLKEM768 +} +``` + +@qmuntal on PR #2342: *"Can we keep `boring` by value instead of by reference? Same as `k`."* + +### 3. Create the gate function + +The gate checks all prerequisites before dispatching to the backend: + +```go +// For a single capability: +func supportsBoringMLKEM768() bool { + return boring.Enabled && rand.IsDefaultReader(rand.Reader) && boring.SupportsMLKEM768() +} + +// For algorithms with parameter sets: +func useBoringMLDSA(params Parameters) (bparams boring.MLDSAParameters, ok bool) { + if !boring.Enabled || !rand.IsDefaultReader(rand.Reader) { + return boring.MLDSAParameters{}, false + } + bparams, ok = boringMLDSAParameters(params) + if !ok || !boring.SupportsMLDSA(bparams) { + return boring.MLDSAParameters{}, false + } + return bparams, true +} +``` + +**Always check capability**, not just `Enabled`: + +```go +// CORRECT +if boring.Enabled && boring.SupportsCurve(c.Params().Name) { ... } + +// WRONG — backend may not support all parameter variations +if boring.Enabled { ... } +``` + +### 4. Dispatch at the top of every public function + +```go +func GenerateKey768() (*DecapsulationKey768, error) { + if supportsBoringMLKEM768() { + key, err := boring.GenerateKeyMLKEM768() + if err != nil { + return nil, err + } + return &DecapsulationKey768{boringKey: key}, nil + } + // ... existing Go implementation +} +``` + +For methods, check which key is populated: + +```go +func (dk *DecapsulationKey768) Bytes() []byte { + if dk.key == nil { + return dk.boringKey.Bytes() + } + return dk.key.Bytes() +} +``` + +### 5. Handle unsupported operations with fallback + +When an operation isn't supported by all backends (e.g., deterministic signing), reconstruct the upstream Go key from the seed: + +```go +func (sk *PrivateKey) goKey() (*mldsa.PrivateKey, error) { + if sk.boringKey.Bytes() == nil { + return &sk.k, nil + } + // Cache the reconstructed key so subsequent calls reuse it. + if sk.k != (mldsa.PrivateKey{}) { + return &sk.k, nil + } + seed := sk.boringKey.Bytes() + var err error + switch sk.boringKey.Parameters().String() { + case "ML-DSA-44": + sk.k, err = mldsa.NewPrivateKey44(seed) + // ... + } + if err != nil { + return nil, err + } + return &sk.k, nil +} +``` + +**Cache the result** — reconstructing keys is expensive. Set `sk.k` on first call so subsequent calls reuse it. @qmuntal: *"Calling NewPrivate[44,65,87] is a bit slow. We could set `sk.k` the first time goKey is used so that the next call reuses the same upstream key."* + +### 6. FIPS 140-only mode + +When `GODEBUG=fips140=only` is set, non-FIPS-approved algorithms must return errors: + +```go +func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { + if fips140only.Enforced() { + return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode") + } + return openssl.NewChaCha20Poly1305(key) +} +``` + +### 7. Update tests + +- **Allocation tests**: Adjust expected allocation counts, don't skip. @qmuntal: *"Don't skip allocation tests. It's better to adjust the allocation expectations when systemcrypto is enabled. This way we catch regressions."* +- **`go/src/go/build/deps_test.go`**: Update if adding new package dependencies. + +### 8. Update documentation + +- Add the new algorithm's platform support status to `eng/_util/cmd/updatecryptodocs/docs.go`, then regenerate the doc: + +```bash +cd eng/_util && go run ./cmd/updatecryptodocs +``` + +This regenerates `eng/doc/CrossPlatformCryptography.md` from the structured data in `docs.go`. **Do not edit `CrossPlatformCryptography.md` by hand** — it is auto-generated. + +@qmuntal on the CSHAKE PR: *"LGTM, I'm only missing an update to CrossPlatformCryptography.md."* + +## Design principles from @qmuntal + +### Push operations into backends, not the integration layer + +> "As a general rule (which has exceptions), we shouldn't implement in this layer operations that upstream implements in `internal/fips140`. For example, OpenSSL supports EVP_PKEY_eq. If CNG/CryptoKit don't provide similar helpers, we can always fallback to comparing the bytes in the backend." + +If the native crypto library (OpenSSL, CNG, CryptoKit) provides a function for an operation like key comparison, implement it in the backend shim rather than reimplementing it in the crypto package integration code. + +### Keep original variable names + +When modifying existing upstream code, preserve the original variable names. @qmuntal on PR #2149: *"Keep the original variable name, `h`."* + +### Use top-level functions over methods for approval checks + +Prefer `func FIPSApprovedHash(h hash.Hash) bool` over adding a method to hash types. @qmuntal: *"This might be slightly better, because then we don't add new methods to the hash objects returned by sha256.New (and friends)."* + +## Which patch gets which changes + +- **`0001-Vendor-external-dependencies.patch`**: `vendor/` directory, `go.mod`, `go.sum`, `modules.txt`, `deps_ignore.go` — all module/vendor changes go here exclusively +- **`0002-Add-crypto-backends.patch`**: All crypto backend integration code — backend shims, algorithm integrations, test modifications, build system changes for systemcrypto +- **Other patches**: See their commit messages. Never duplicate changes across patches. + +## Vendor changes workflow + +If your crypto change requires a new or updated backend module dependency: + +1. Make the vendor changes (`go.mod`, `go.sum`, `vendor/`) in the submodule +2. Commit them as a separate commit +3. Squash that commit into the `0001-Vendor-external-dependencies.patch` commit during `git go-patch rebase` +4. Squash the crypto code changes into the `0002-Add-crypto-backends.patch` commit +5. Run `git go-patch extract` to regenerate both patches diff --git a/.github/workflows/check-line-endings.yml b/.github/workflows/check-line-endings.yml index ec9c12e13e1..faa5f00a596 100644 --- a/.github/workflows/check-line-endings.yml +++ b/.github/workflows/check-line-endings.yml @@ -13,4 +13,4 @@ permissions: jobs: check-line-endings: - uses: microsoft/go-infra/.github/workflows/check-line-endings.yml@f4e3833067a0e64dbf9581e3e4d2df2a353f6dd0 # v0.0.13 + uses: microsoft/go-infra/.github/workflows/check-line-endings.yml@eae52708dd530100eb695850bfc7f994dd9340d3 # v0.0.14 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 110248f748a..f5e553aaa7b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,19 +20,19 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Initialize CodeQL - uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/autobuild@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 with: working-directory: eng/_util - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/analyze@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 with: category: /language:go diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 3a5bbf562b9..9cfaba96e1b 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -16,7 +16,7 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Run golangci-lint working-directory: eng/_util run: docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v2.10 golangci-lint run -v diff --git a/.github/workflows/patch-build.yml b/.github/workflows/patch-build.yml index 4c947c0a949..df022fd23c3 100644 --- a/.github/workflows/patch-build.yml +++ b/.github/workflows/patch-build.yml @@ -23,7 +23,7 @@ jobs: patches: ${{ steps.list.outputs.patches }} steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Write patch matrix id: list @@ -38,7 +38,7 @@ jobs: patch: ${{ fromJson(needs.list_patches.outputs.patches) }} steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: submodules: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7682d7f3018..d68a61c7f44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - run: pwsh eng/run.ps1 submodule-refresh -shallow diff --git a/README.md b/README.md index 808d23a4eb8..a8226310f2e 100644 --- a/README.md +++ b/README.md @@ -28,32 +28,33 @@ The submodule is updated regularly to the latest commit available in both the up ## Support -This project follows the upstream Go -[Release Policy](https://go.dev/doc/devel/release#policy). -This means we support each major release (1.X) until there are two newer major -releases. A new Go major version is -[released every six months](https://github.com/golang/go/wiki/Go-Release-Cycle), -so each Go major version is supported for about one year. - -When upstream Go releases a new minor version (1.X.Y), we release a -corresponding microsoft/go version that may also include fork-specific changes. +See [SUPPORT.md](SUPPORT.md) for more information about reporting bugs, requesting features, and asking questions. + +There are a few additional support resources internal to Microsoft: + +* [(Microsoft-internal) Languages at Microsoft: Introduction to Go](https://eng.ms/docs/more/languages-at-microsoft/go/articles/overview). +* [(Microsoft-internal) Languages at Microsoft: Get Help with Go](https://eng.ms/docs/more/languages-at-microsoft/go/articles/support). + * Includes internal Microsoft support channels such as an email contact for our team and a community Teams group. + +## Release cycle and policy + +This project follows the upstream Go [Release Policy](https://go.dev/doc/devel/release#policy). +This means we support each major release (1.X) until there are two newer major releases. +A new Go major version is [released every six months](https://github.com/golang/go/wiki/Go-Release-Cycle), so each Go major version is supported for about one year. + +When upstream Go releases a new minor version (1.X.Y), we release a corresponding microsoft/go version that may also include fork-specific changes. This normally happens once a month. At any time, we may release a new revision (1.X.Y-Z) to fix an issue without waiting for the next upstream minor release. Revision releases are uncommon. -Each microsoft/go release is announced at the -[Microsoft for Go Developers](https://devblogs.microsoft.com/go/) blog. -Check out the upstream [golang-announce mailing list](https://groups.google.com/g/golang-announce) -for a summary of the changes in each upstream Go version. +We announce each Microsoft build of Go release through the following channels: -See [SUPPORT.md](SUPPORT.md) for more information about reporting bugs, requesting features, and asking questions. - -There are a few additional support resources internal to Microsoft: +* [Microsoft for Go Developers blog](https://devblogs.microsoft.com/go/). +* [Microsoft-internal email distribution list 📧 (instant join link)](https://idwebelements.microsoft.com/GroupManagement.aspx?Group=golang-announce&Operation=join). -* [Languages at Microsoft: Go](https://eng.ms/docs/more/languages-at-microsoft/go/articles/overview). -* [A Microsoft-internal email distribution list 📧 (instant join link)](https://idwebelements.microsoft.com/GroupManagement.aspx?Group=golang-announce&Operation=join) - for release announcements. +The Go team announces upstream releases on the [golang-announce mailing list](https://groups.google.com/g/golang-announce). +These announcement emails include a carefully written summary of the changes that may not be found elsewhere. ## Download and install @@ -76,7 +77,7 @@ The [Installation](eng/doc/Installation.md) documentation contains sections desc * [Azure Pipelines `GoTool@0` task](eng/doc/Installation.md#azure-pipelines-gotool0-task) * [GitHub Actions `setup-go` action](eng/doc/Installation.md#github-actions-setup-go-action) * [The `go-install.ps1` script](eng/doc/Installation.md#the-go-installps1-script) -* [Binary archive](eng/doc/Installation.md#binary-archive) +* [Binary archive](eng/doc/Installation.md#binary-archive) (`tar.gz` and `zip`) * [Build from source](eng/doc/Installation.md#build-from-source) ## Contributing diff --git a/SUPPORT.md b/SUPPORT.md index 5594fc5c2ff..a5c8aaa4b58 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -4,6 +4,9 @@ For help and questions about the Go programming language and tools, visit the of Take a look at the [Migration Guide](eng/doc/MigrationGuide.md) for more information about migrating from the official build of Go to the Microsoft build of Go, specifically. +> [!TIP] +> Additional support options internal to Microsoft are listed in the ["Support" section of the README file](README.md#support). + ## How to file issues and get help This project uses GitHub Issues to track bugs and feature requests. Please search the existing diff --git a/eng/_util/go.mod b/eng/_util/go.mod index 353a3db41f9..e9329ee1503 100644 --- a/eng/_util/go.mod +++ b/eng/_util/go.mod @@ -8,9 +8,9 @@ go 1.25.0 require ( github.com/golang-jwt/jwt/v5 v5.3.1 - github.com/microsoft/go-infra v0.0.13 + github.com/microsoft/go-infra v0.0.14 github.com/microsoft/go-infra/goinstallscript v1.2.0 - golang.org/x/net v0.54.0 + golang.org/x/net v0.55.0 ) require ( diff --git a/eng/_util/go.sum b/eng/_util/go.sum index 318abc4e41f..0b092108aaf 100644 --- a/eng/_util/go.sum +++ b/eng/_util/go.sum @@ -31,8 +31,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 h1:YH424zrwLTlyHSH/GzLMJeu5zhYVZSx5RQxGKm1h96s= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= -github.com/microsoft/go-infra v0.0.13 h1:s7ymSsDJagh35CYuJDc8GJdBqtZ5sE696oi7mxRMnvI= -github.com/microsoft/go-infra v0.0.13/go.mod h1:+NF5K79FJNpFKRPX7J9g9tZ2NFZa9dM751xWPpF3IgY= +github.com/microsoft/go-infra v0.0.14 h1:JfMAEtUWk2sAKNyYirdph8BoKrjCKlt/vMloBy70rtg= +github.com/microsoft/go-infra v0.0.14/go.mod h1:+NF5K79FJNpFKRPX7J9g9tZ2NFZa9dM751xWPpF3IgY= github.com/microsoft/go-infra/goinstallscript v1.2.0 h1:ArYnZHsmv0jnpeDZdFACBUxSmhmYl+Vof8sfk19aYZI= github.com/microsoft/go-infra/goinstallscript v1.2.0/go.mod h1:SFsdKAEHdmGsGoh8FkksVaxoQ3rnnJ/TBqN09Ml/0Cw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -53,8 +53,8 @@ go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= diff --git a/eng/doc/AdditionalFeatures.md b/eng/doc/AdditionalFeatures.md index b109c7aab82..f6081880aa9 100644 --- a/eng/doc/AdditionalFeatures.md +++ b/eng/doc/AdditionalFeatures.md @@ -11,7 +11,8 @@ If it's critical to you to understand the exact implementation details of all ch The Microsoft build of Go collects [opt-out (enabled by default) toolset telemetry](https://devblogs.microsoft.com/go/microsoft-go-telemetry/). -See the [Data Collection policy for the Microsoft build of Go](/README.md#data-collection). +See the [Telemetry documentation](/eng/doc/Telemetry.md) for details on what is collected and how to opt out. +See also the [Data Collection policy for the Microsoft build of Go](/README.md#data-collection). ## System-provided cryptography backend diff --git a/eng/doc/MigrationGuide.md b/eng/doc/MigrationGuide.md index c4918c63517..2792447ea40 100644 --- a/eng/doc/MigrationGuide.md +++ b/eng/doc/MigrationGuide.md @@ -8,7 +8,8 @@ It's a fork, and some runtime behavior slightly differs, but in most cases it ha We expect that most projects don't require any Go code changes to work with the Microsoft build of Go. Note that the Microsoft build of Go has [toolset telemetry enabled by default](https://devblogs.microsoft.com/go/microsoft-go-telemetry/) (opt-out telemetry). -See [the Data Collection policy for the Microsoft build of Go](/README.md#data-collection). +See the [Telemetry documentation](/eng/doc/Telemetry.md) for details on what is collected and how to opt out. +See also [the Data Collection policy for the Microsoft build of Go](/README.md#data-collection). ## Quick start diff --git a/eng/doc/README.md b/eng/doc/README.md index aaed7c9848b..d974d1daa6c 100644 --- a/eng/doc/README.md +++ b/eng/doc/README.md @@ -1,9 +1,23 @@ -This directory, `/eng/doc`, contains documents describing the Microsoft -infrastructure used to build Go, in particular any designs that are not obvious -by reading the infrastructure code itself. +# `/eng/doc` - Documentation for the Microsoft build of Go -For cryptography information, see the [CrossPlatformCryptography.md](CrossPlatformCryptography.md) doc. -For dev scenario documentation, see the [DeveloperGuide.md](DeveloperGuide.md) doc. +This directory, `/eng/doc`, contains detailed documents describing the Microsoft build of Go. -The [Downloads.md](Downloads.md) doc contains a table of links to the latest assets for each supported Go release branch. -The [release-branch-links.json](release-branch-links.json) file contains the same data in JSON format suitable for parsing. +> [!IMPORTANT] +> These documents are only actively maintained in the `microsoft/main` branch. +> If you're viewing this file in a release branch, it may be stale. +> [If you're not sure, click here to go to the maintained version of this document.](https://github.com/microsoft/go/blob/microsoft/main/eng/doc/README.md) + +## Setup + +* [Migration Guide](MigrationGuide.md) +* [Installation](Installation.md) + * [Downloading a binary release](Downloads.md) +* [Developer Guide](DeveloperGuide.md) (for new contributors to the Microsoft build of Go) + +## Features + +* [Additional Features Provided by the Microsoft build of Go](AdditionalFeatures.md) +* [Cross-Platform Cryptography in the Microsoft build of Go](CrossPlatformCryptography.md) +* [Microsoft Toolset Identification](MicrosoftToolsetIdentification.md) +* [No-cgo OpenSSL Backend](NocgoOpenSSL.md) +* [Telemetry in the Microsoft build of Go](Telemetry.md) diff --git a/eng/doc/Telemetry.md b/eng/doc/Telemetry.md new file mode 100644 index 00000000000..3877a6dd6a1 --- /dev/null +++ b/eng/doc/Telemetry.md @@ -0,0 +1,82 @@ +# Telemetry in the Microsoft build of Go + +The Microsoft build of Go collects opt-out (enabled by default) toolset telemetry. +Telemetry is sent to Microsoft via Azure Application Insights when `go build`, `go install`, or `go run` is executed. +No telemetry is collected for other `go` subcommands (e.g. `go test`, `go vet`, `go fmt`). + +For the blog post announcing this feature, see [Microsoft build of Go Telemetry](https://devblogs.microsoft.com/go/microsoft-go-telemetry/). + +## What is collected + +Telemetry data consists of **counters** (simple event names that are incremented) and **properties** (key-value custom dimensions attached to a counter event). +No source code, file paths, module contents, or personally identifiable information is collected. +Each telemetry session is assigned a random session ID that cannot be linked back to a user. + +The counters and properties collected are defined in the [telemetry upload configuration](https://github.com/microsoft/go-infra/blob/main/telemetry/config/config.json) and are listed below. + +### Invocation counter + +| Counter | Description | +|---|---| +| `go/invocations` | Incremented once per `go` command invocation. | + +#### Properties on `go/invocations` + +| Property | Description | +|---|---| +| `msgo/module/hash` | A SHA-256 hash of the Go module path from the nearest `go.mod` file, found using [`modload.FindGoMod`](https://pkg.go.dev/cmd/go/internal/modload#FindGoMod). Only sent when a `go.mod` file is found. | + +### Subcommand + +| Counter | Description | +|---|---| +| `go/subcommand:{build,install,run}` | Which subcommand was used. | + +### Target platform + +| Counter | Description | +|---|---| +| `go/platform/target/port:-` | The target platform port (e.g. `linux-amd64`, `windows-arm64`). | +| `go/cgo:{enabled,disabled}` | Whether cgo was enabled for the build. | + +### GOEXPERIMENT settings + +| Counter | Description | +|---|---| +| `go/goexperiment:` | Each active `GOEXPERIMENT` value that differs from the baseline. Only Microsoft-specific experiments are uploaded: `systemcrypto`, `nosystemcrypto`, `opensslcrypto`, `cngcrypto`, `darwincrypto`, `ms_nocgo_opensslcrypto`, and `ms_tls_config_schannel`. | + +### CI environment detection + +| Counter | Description | +|---|---| +| `msgo/ci:` | Detected CI provider, if any. Values: `azdo`, `github`, `gitlab`, `appveyor`, `travis`, `circleci`, `aws_codebuild`, `jenkins`, `teamcity`, `google_cloud_build`. Detection is based on well-known environment variables set by each CI system. | + +### System cryptography + +| Counter | Description | +|---|---| +| `msgo/systemcrypto:{enabled,disabled}` | Whether the system cryptography backend is enabled on platforms that support it. | + +## What is NOT collected + +- No source code or file contents. +- No file paths from the local file system. +- No textual module names or paths (a one-way SHA-256 hash of the module path is collected). +- No environment variable values (only the presence of specific CI-related variables is checked). +- No network or authentication information. +- No personally identifiable information (PII). + +## How to opt out + +Set the `MS_GOTOOLCHAIN_TELEMETRY_ENABLED` environment variable to `0`. + +This completely disables Microsoft telemetry collection. When disabled, no data is sent to Application Insights. + +> [!NOTE] +> This setting only controls Microsoft-specific telemetry. The upstream Go toolchain has its own [telemetry system](https://go.dev/doc/telemetry) controlled separately via `go telemetry`. + +## Privacy + +Microsoft's privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. + +See the [Data Collection policy](/README.md#data-collection) in the repository README for more information. diff --git a/eng/doc/fips/README.md b/eng/doc/fips/README.md index 6f766e6f436..92819eab3ae 100644 --- a/eng/doc/fips/README.md +++ b/eng/doc/fips/README.md @@ -461,10 +461,8 @@ This list of major changes is intended for quick reference and for access to his - The per-platform GOEXPERIMENTs (`opensslcrypto`, `cngcrypto`, `darwincrypto`) have been removed. - Using any of the removed experiments will result in a build error. - The `systemcrypto` GOEXPERIMENT has been the preferred way to select a crypto backend since it was introduced in Go 1.21. It is now the only way. - - The build tags (build constraints) associated with the removed GOEXPERIMENTs are no longer supported. - - The per-platform tags are not set by the Microsoft build of Go when `systemcrypto` is enabled. - - Manually using `-tags` to enable a per-platform backend tag no longer has any effect on the standard library. - - The `goexperiment.systemcrypto` build tag remains supported, and its behavior has not changed. + - The build tags associated with the removed GOEXPERIMENTs remain supported for legacy source compatibility. + - The `goexperiment.systemcrypto` build tag remains supported, and its behavior has not changed. ### Go 1.26.3 diff --git a/eng/pipeline/stages/builders-to-stages.yml b/eng/pipeline/stages/builders-to-stages.yml index 98de2fedb81..28563e67158 100644 --- a/eng/pipeline/stages/builders-to-stages.yml +++ b/eng/pipeline/stages/builders-to-stages.yml @@ -8,7 +8,7 @@ parameters: - name: ctx type: object - # [] of { id, os, arch, hostarch, config, distro?, experiment?, broken? } + # [] of { id, os, arch, hostArch, config, distro?, experiment?, nosystemcrypto?, fips?, broken? } - name: builders type: object # If true, include a signing stage+job that depends on all 'buildandpack' builder jobs finishing. diff --git a/eng/pipeline/stages/go-builder-matrix-stages.yml b/eng/pipeline/stages/go-builder-matrix-stages.yml index b556c35f04e..9e82b766778 100644 --- a/eng/pipeline/stages/go-builder-matrix-stages.yml +++ b/eng/pipeline/stages/go-builder-matrix-stages.yml @@ -77,50 +77,50 @@ stages: - ${{ if parameters.includeArm64Host }}: - { os: linux, arch: arm64, config: buildandpack } - ${{ if parameters.innerloop }}: - - { experiment: nosystemcrypto, os: darwin, arch: amd64, config: devscript } - - { experiment: nosystemcrypto, os: darwin, arch: amd64, config: test } - - { experiment: systemcrypto, os: darwin, arch: amd64, config: test } - - { experiment: systemcrypto, os: darwin, arch: amd64, config: nocgo } - - { experiment: systemcrypto, os: darwin, arch: amd64, config: test, fips: true } - - { experiment: nosystemcrypto, os: darwin, arch: arm64, config: devscript } - - { experiment: nosystemcrypto, os: darwin, arch: arm64, config: test } - - { experiment: systemcrypto, os: darwin, arch: arm64, config: test } - - { experiment: systemcrypto, os: darwin, arch: arm64, config: test, fips: true } - # - { experiment: nosystemcrypto, os: windows, arch: arm64, config: test } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: devscript } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: test } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: test, distro: ubuntu } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: test, distro: azurelinux3 } - - { experiment: systemcrypto, os: linux, arch: amd64, config: test } - - { experiment: systemcrypto, os: linux, arch: amd64, config: test, fips: true } - - { experiment: systemcrypto, os: linux, arch: amd64, config: nocgo, fips: true } - - { experiment: systemcrypto, os: linux, arch: amd64, config: test, distro: ubuntu } - - { experiment: systemcrypto, os: linux, arch: amd64, config: nocgo, distro: ubuntu } - - { experiment: systemcrypto, os: linux, arch: amd64, config: test, distro: azurelinux3 } - - { experiment: systemcrypto, os: linux, arch: amd64, config: nocgo, distro: azurelinux3 } - - { experiment: systemcrypto, os: linux, arch: amd64, config: test, distro: azurelinux3, fips: true } - - { experiment: systemcrypto, os: linux, arch: amd64, config: nocgo, distro: azurelinux3, fips: true } - - { experiment: nosystemcrypto, os: windows, arch: amd64, config: devscript } - - { experiment: nosystemcrypto, os: windows, arch: amd64, config: test } - - { experiment: systemcrypto, os: windows, arch: amd64, config: test } - - { experiment: systemcrypto, os: windows, arch: amd64, config: test, fips: true } + - { nosystemcrypto: true, os: darwin, arch: amd64, config: devscript } + - { nosystemcrypto: true, os: darwin, arch: amd64, config: test } + - { os: darwin, arch: amd64, config: test } + - { os: darwin, arch: amd64, config: nocgo } + - { os: darwin, arch: amd64, config: test, fips: true } + - { nosystemcrypto: true, os: darwin, arch: arm64, config: devscript } + - { nosystemcrypto: true, os: darwin, arch: arm64, config: test } + - { os: darwin, arch: arm64, config: test } + - { os: darwin, arch: arm64, config: test, fips: true } + # - { nosystemcrypto: true, os: windows, arch: arm64, config: test } + - { nosystemcrypto: true, os: linux, arch: amd64, config: devscript } + - { nosystemcrypto: true, os: linux, arch: amd64, config: test } + - { nosystemcrypto: true, os: linux, arch: amd64, config: test, distro: ubuntu } + - { nosystemcrypto: true, os: linux, arch: amd64, config: test, distro: azurelinux3 } + - { os: linux, arch: amd64, config: test } + - { os: linux, arch: amd64, config: test, fips: true } + - { os: linux, arch: amd64, config: nocgo, fips: true } + - { os: linux, arch: amd64, config: test, distro: ubuntu } + - { os: linux, arch: amd64, config: nocgo, distro: ubuntu } + - { os: linux, arch: amd64, config: test, distro: azurelinux3 } + - { os: linux, arch: amd64, config: nocgo, distro: azurelinux3 } + - { os: linux, arch: amd64, config: test, distro: azurelinux3, fips: true } + - { os: linux, arch: amd64, config: nocgo, distro: azurelinux3, fips: true } + - { nosystemcrypto: true, os: windows, arch: amd64, config: devscript } + - { nosystemcrypto: true, os: windows, arch: amd64, config: test } + - { os: windows, arch: amd64, config: test } + - { os: windows, arch: amd64, config: test, fips: true } - { experiment: ms_tls_config_schannel, os: windows, arch: amd64, config: test } # Test that buildandpack works on Windows x86-32, but don't release it. - - { experiment: nosystemcrypto, os: windows, hostArch: amd64, arch: 386, config: buildandpack } + - { nosystemcrypto: true, os: windows, hostArch: amd64, arch: 386, config: buildandpack } - ${{ if parameters.outerloop }}: # Upstream builders. - # - { experiment: nosystemcrypto, os: linux, arch: amd64, config: clang } https://github.com/microsoft/go/issues/342 - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: longtest } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: nocgo } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: noopt } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: race } - # - { os: experiment: nosystemcrypto, linux, arch: amd64, config: racecompile } https://github.com/microsoft/go/issues/54 - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: regabi } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: ssacheck } - - { experiment: nosystemcrypto, os: linux, arch: amd64, config: staticlockranking } - # - { experiment: systemcrypto, os: linux, arch: amd64, config: clang } https://github.com/microsoft/go/issues/342 - - { experiment: systemcrypto, os: linux, arch: amd64, config: longtest } - - { experiment: systemcrypto, os: linux, arch: amd64, config: race } - - { experiment: systemcrypto, os: linux, arch: amd64, config: regabi } - - { experiment: systemcrypto, os: linux, arch: amd64, config: ssacheck } - - { experiment: systemcrypto, os: linux, arch: amd64, config: staticlockranking } + # - { nosystemcrypto: true, os: linux, arch: amd64, config: clang } https://github.com/microsoft/go/issues/342 + - { nosystemcrypto: true, os: linux, arch: amd64, config: longtest } + - { nosystemcrypto: true, os: linux, arch: amd64, config: nocgo } + - { nosystemcrypto: true, os: linux, arch: amd64, config: noopt } + - { nosystemcrypto: true, os: linux, arch: amd64, config: race } + # - { nosystemcrypto: true, os: linux, arch: amd64, config: racecompile } https://github.com/microsoft/go/issues/54 + - { nosystemcrypto: true, os: linux, arch: amd64, config: regabi } + - { nosystemcrypto: true, os: linux, arch: amd64, config: ssacheck } + - { nosystemcrypto: true, os: linux, arch: amd64, config: staticlockranking } + # - { os: linux, arch: amd64, config: clang } https://github.com/microsoft/go/issues/342 + - { os: linux, arch: amd64, config: longtest } + - { os: linux, arch: amd64, config: race } + - { os: linux, arch: amd64, config: regabi } + - { os: linux, arch: amd64, config: ssacheck } + - { os: linux, arch: amd64, config: staticlockranking } diff --git a/eng/pipeline/stages/run-codeql.yml b/eng/pipeline/stages/run-codeql.yml index f29e84b46c4..b302e4195bc 100644 --- a/eng/pipeline/stages/run-codeql.yml +++ b/eng/pipeline/stages/run-codeql.yml @@ -8,7 +8,7 @@ parameters: - name: ctx type: object - # { id, os, arch, hostArch, config, distro?, experiment?, fips?, broken? } + # { id, os, arch, hostArch, config, distro?, experiment?, nosystemcrypto?, fips?, broken? } - name: builder type: object @@ -45,11 +45,11 @@ stages: - stage: ${{ parameters.builder.id }} # For display name, try for readability. Use some parameters set by # shorthand-builders-to-builders.yml that let us add some formatting. - displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.fipsAcronym }} + displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.nosystemcryptoBrackets }} ${{ parameters.builder.fipsAcronym }} dependsOn: [] jobs: - job: ${{ parameters.builder.id }} - displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.fipsAcronym }} + displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.nosystemcryptoBrackets }} ${{ parameters.builder.fipsAcronym }} workspace: clean: all diff --git a/eng/pipeline/stages/run-stage.yml b/eng/pipeline/stages/run-stage.yml index cbf66ed3904..fe2455c0372 100644 --- a/eng/pipeline/stages/run-stage.yml +++ b/eng/pipeline/stages/run-stage.yml @@ -8,7 +8,7 @@ parameters: - name: ctx type: object - # { id, os, arch, hostArch, config, distro?, experiment?, fips?, broken? } + # { id, os, arch, hostArch, config, distro?, experiment?, nosystemcrypto?, fips?, broken? } - name: builder type: object @@ -38,11 +38,11 @@ stages: - stage: ${{ parameters.builder.id }} # For display name, try for readability. Use some parameters set by # shorthand-builders-to-builders.yml that let us add some formatting. - displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.fipsAcronym }} + displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.nosystemcryptoBrackets }} ${{ parameters.builder.fipsAcronym }} dependsOn: [] jobs: - job: ${{ parameters.builder.id }} - displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.fipsAcronym }} + displayName: ${{ parameters.builder.os }}-${{ parameters.builder.arch }} ${{ parameters.builder.hostParens}} ${{ parameters.builder.config }} ${{ parameters.builder.distroParens}} ${{ parameters.builder.experimentBrackets }} ${{ parameters.builder.nosystemcryptoBrackets }} ${{ parameters.builder.fipsAcronym }} workspace: clean: all @@ -180,6 +180,8 @@ stages: PACK_SOURCE_ARG: '-packsource' ${{ if eq(variables.createPDB, true) }}: CREATE_PDB_ARG: '-pdb' + ${{ if parameters.builder.nosystemcrypto }}: + MS_GO_NOSYSTEMCRYPTO: 1 displayName: Build and Pack # We want to create a checksum as early as possible, but Windows signing involves @@ -237,6 +239,9 @@ stages: -builder '${{ parameters.builder.os }}-${{ parameters.builder.arch }}-${{ parameters.builder.config }}' ` $(if ('${{ parameters.builder.experiment }}') { '-experiment'; '${{ parameters.builder.experiment }}' }) ` $(if ('${{ parameters.builder.fips }}') { '-fipsmode' }) + ${{ if parameters.builder.nosystemcrypto }}: + env: + MS_GO_NOSYSTEMCRYPTO: 1 displayName: Build # Run each test retry attempt in its own step. Benefits over a single step: @@ -267,6 +272,9 @@ stages: $(if ('${{ parameters.builder.fips }}') { '-fipsmode' }) ` -junitout '$(Build.SourcesDirectory)/eng/artifacts/RawTestOutput/TestResults-attempt-${{ attempt }}.xml' ` -rawtestout '$(Build.SourcesDirectory)/eng/artifacts/RawTestOutput/raw-json-attempt-${{ attempt }}.txt' + ${{ if parameters.builder.nosystemcrypto }}: + env: + MS_GO_NOSYSTEMCRYPTO: 1 ${{ if eq(length(parameters.retryAttempts), 1) }}: displayName: Test ${{ else }}: diff --git a/eng/pipeline/stages/shorthand-builders-to-builders.yml b/eng/pipeline/stages/shorthand-builders-to-builders.yml index 50ddb375a48..10357ece7e8 100644 --- a/eng/pipeline/stages/shorthand-builders-to-builders.yml +++ b/eng/pipeline/stages/shorthand-builders-to-builders.yml @@ -14,14 +14,14 @@ parameters: - name: ctx type: object - # [] of { os, arch, hostArch, config, distro?, experiment?, broken? } + # [] of { os, arch, hostArch, config, distro?, experiment?, nosystemcrypto?, fips?, broken? } # If hostArch is not defined, defaults to the arch value. # The job ID is generated based on these values. - name: shorthandBuilders type: object # The inner jobs template to pass the filed-out builders into. # - # It should accept parameter "builders", [] of { id, os, arch, hostArch, config, distro?, fips?, broken? } + # It should accept parameter "builders", [] of { id, os, arch, hostArch, config, distro?, experiment?, nosystemcrypto?, fips?, broken? } - name: jobsTemplate type: string - name: jobsParameters @@ -38,7 +38,7 @@ stages: - ${{ insert }}: ${{ builder }} # Use 'default' in place of null to define ID. This value just needs to be unique and # only contain "[A-z_]+". - id: ${{ builder.os }}_${{ coalesce(builder.distro, 'default') }}_${{ coalesce(builder.hostArch, 'default') }}_${{ builder.arch }}_${{ builder.config }}_${{ coalesce(replace(builder.experiment, ',', '_'), 'default') }}_${{ coalesce(builder.fips, false) }} + id: ${{ builder.os }}_${{ coalesce(builder.distro, 'default') }}_${{ coalesce(builder.hostArch, 'default') }}_${{ builder.arch }}_${{ builder.config }}_${{ coalesce(replace(builder.experiment, ',', '_'), 'default') }}_${{ coalesce(builder.nosystemcrypto, false) }}_${{ coalesce(builder.fips, false) }} ${{ if not(builder.hostArch) }}: hostArch: ${{ builder.arch }} ${{ if and(not(builder.distro), eq(builder.os, 'windows')) }}: @@ -51,5 +51,7 @@ stages: hostParens: (${{ builder.hostArch }} host) ${{ if builder.experiment }}: experimentBrackets: '[${{ builder.experiment }}]' + ${{ if builder.nosystemcrypto }}: + nosystemcryptoBrackets: '[nosystemcrypto]' ${{ if builder.fips }}: fipsAcronym: 'FIPS' diff --git a/eng/pipeline/stages/sign-stage.yml b/eng/pipeline/stages/sign-stage.yml index 77191b24075..45d260bb8e8 100644 --- a/eng/pipeline/stages/sign-stage.yml +++ b/eng/pipeline/stages/sign-stage.yml @@ -14,7 +14,7 @@ parameters: - name: pool type: object - # [] of { id, os, arch, config, distro?, experiment?, broken? } + # [] of { id, os, arch, config, distro?, experiment?, nosystemcrypto?, fips?, broken? } - name: builders type: object diff --git a/go b/go index 04ed01963e1..0252b4be030 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit 04ed01963e1b2ec2f801416988cb7d57b7e13b36 +Subproject commit 0252b4be0300559f6ff0032636d84aa0acb71e83 diff --git a/patches/0001-Vendor-external-dependencies.patch b/patches/0001-Vendor-external-dependencies.patch index 3411b570fcb..58d2cf993e9 100644 --- a/patches/0001-Vendor-external-dependencies.patch +++ b/patches/0001-Vendor-external-dependencies.patch @@ -13,12 +13,13 @@ Use a 'go' that was recently built by the current branch to ensure stable result .../internal/telemetry/counter/deps_ignore.go | 17 + .../microsoft/go-infra/telemetry/LICENSE | 21 + .../microsoft/go-infra/telemetry/README.md | 9 + + .../microsoft/go-infra/telemetry/ci.go | 97 + .../go-infra/telemetry/config/LICENSE | 21 + .../go-infra/telemetry/config/config.go | 11 + - .../go-infra/telemetry/config/config.json | 76 + - .../go-infra/telemetry/counter/counter.go | 63 + + .../go-infra/telemetry/config/config.json | 77 + + .../go-infra/telemetry/counter/counter.go | 71 + .../telemetry/internal/appinsights/README.md | 12 + - .../telemetry/internal/appinsights/client.go | 192 ++ + .../telemetry/internal/appinsights/client.go | 200 ++ .../internal/appinsights/inmemorychannel.go | 349 +++ .../internal/contracts/contexttagkeys.go | 50 + .../internal/contracts/envelope.go | 123 + @@ -27,10 +28,10 @@ Use a 'go' that was recently built by the current branch to ensure stable result .../telemetry/internal/appinsights/package.go | 13 + .../internal/appinsights/telemetrycontext.go | 44 + .../internal/appinsights/transmitter.go | 172 ++ - .../telemetry/internal/config/config.go | 78 + + .../telemetry/internal/config/config.go | 80 + .../telemetry/internal/telemetry/proginfo.go | 46 + .../telemetry/internal/telemetry/telemetry.go | 32 + - .../microsoft/go-infra/telemetry/telemetry.go | 144 + + .../microsoft/go-infra/telemetry/telemetry.go | 168 ++ src/cmd/vendor/modules.txt | 11 + src/crypto/internal/backend/deps_ignore.go | 22 + src/go.mod | 6 + @@ -206,39 +207,39 @@ Use a 'go' that was recently built by the current branch to ensure stable result .../go-crypto-openssl/openssl/aes.go | 158 ++ .../go-crypto-openssl/openssl/big.go | 14 + .../openssl/chacha20poly1305.go | 152 + - .../go-crypto-openssl/openssl/cipher.go | 666 +++++ + .../go-crypto-openssl/openssl/cipher.go | 665 +++++ .../go-crypto-openssl/openssl/const.go | 109 + .../go-crypto-openssl/openssl/cshake.go | 256 ++ .../go-crypto-openssl/openssl/des.go | 121 + - .../go-crypto-openssl/openssl/dsa.go | 306 ++ + .../go-crypto-openssl/openssl/dsa.go | 296 ++ .../microsoft/go-crypto-openssl/openssl/ec.go | 132 + - .../go-crypto-openssl/openssl/ecdh.go | 341 +++ - .../go-crypto-openssl/openssl/ecdsa.go | 220 ++ + .../go-crypto-openssl/openssl/ecdh.go | 331 +++ + .../go-crypto-openssl/openssl/ecdsa.go | 212 ++ .../go-crypto-openssl/openssl/ed25519.go | 209 ++ - .../go-crypto-openssl/openssl/evp.go | 615 ++++ + .../go-crypto-openssl/openssl/evp.go | 616 ++++ .../go-crypto-openssl/openssl/hash.go | 521 ++++ - .../go-crypto-openssl/openssl/hkdf.go | 330 +++ - .../go-crypto-openssl/openssl/hmac.go | 280 ++ + .../go-crypto-openssl/openssl/hkdf.go | 312 +++ + .../go-crypto-openssl/openssl/hmac.go | 269 ++ .../go-crypto-openssl/openssl/mldsa.go | 449 +++ .../go-crypto-openssl/openssl/mlkem.go | 369 +++ - .../go-crypto-openssl/openssl/openssl.go | 183 ++ + .../go-crypto-openssl/openssl/openssl.go | 182 ++ .../go-crypto-openssl/openssl/openssl_cgo.go | 17 + .../openssl/openssl_nocgo.go | 35 + .../go-crypto-openssl/openssl/params.go | 191 ++ - .../go-crypto-openssl/openssl/pbkdf2.go | 86 + - .../openssl/provideropenssl.go | 250 ++ + .../go-crypto-openssl/openssl/pbkdf2.go | 82 + + .../openssl/provideropenssl.go | 257 ++ .../openssl/providersymcrypt.go | 331 +++ .../go-crypto-openssl/openssl/rand.go | 37 + .../go-crypto-openssl/openssl/rc4.go | 76 + - .../go-crypto-openssl/openssl/rsa.go | 712 +++++ - .../go-crypto-openssl/openssl/tls1prf.go | 157 ++ + .../go-crypto-openssl/openssl/rsa.go | 706 +++++ + .../go-crypto-openssl/openssl/tls1prf.go | 147 + .../go-crypto-openssl/openssl/zaes.go | 87 + - .../go-crypto-openssl/osslsetup/fips.go | 69 + + .../go-crypto-openssl/osslsetup/fips.go | 115 + .../go-crypto-openssl/osslsetup/init.go | 170 ++ .../osslsetup/init_cgo_unix.go | 34 + .../osslsetup/init_nocgo_unix.go | 35 + .../osslsetup/init_windows.go | 37 + - .../go-crypto-openssl/osslsetup/osslsetup.go | 109 + + .../go-crypto-openssl/osslsetup/osslsetup.go | 123 + .../osslsetup/osslsetup_cgo.go | 12 + .../osslsetup/osslsetup_nocgo.go | 24 + .../microsoft/go-crypto-winnative/LICENSE | 21 + @@ -270,10 +271,11 @@ Use a 'go' that was recently built by the current branch to ensure stable result .../internal/subtle/aliasing.go | 32 + .../internal/sysdll/sys_windows.go | 55 + src/vendor/modules.txt | 23 + - 262 files changed, 35630 insertions(+), 7 deletions(-) + 263 files changed, 35759 insertions(+), 7 deletions(-) create mode 100644 src/cmd/internal/telemetry/counter/deps_ignore.go create mode 100644 src/cmd/vendor/github.com/microsoft/go-infra/telemetry/LICENSE create mode 100644 src/cmd/vendor/github.com/microsoft/go-infra/telemetry/README.md + create mode 100644 src/cmd/vendor/github.com/microsoft/go-infra/telemetry/ci.go create mode 100644 src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/LICENSE create mode 100644 src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.go create mode 100644 src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json @@ -527,33 +529,33 @@ Use a 'go' that was recently built by the current branch to ensure stable result create mode 100644 src/vendor/github.com/microsoft/go-crypto-winnative/internal/sysdll/sys_windows.go diff --git a/src/cmd/go.mod b/src/cmd/go.mod -index df4fba11865b84..c16e08c45e76b8 100644 +index 82ceadb04a273a..b7fe75a52e69f8 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -4,6 +4,8 @@ go 1.27 require ( - github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 -+ github.com/microsoft/go-infra/telemetry v0.0.0-20260513084116-8db604e72b76 -+ github.com/microsoft/go-infra/telemetry/config v0.0.0-20260513084116-8db604e72b76 - golang.org/x/arch v0.27.1-0.20260513003155-2ebc08890589 - golang.org/x/build v0.0.0-20260122183339-3ba88df37c64 - golang.org/x/mod v0.36.1-0.20260513122029-343ee60345a1 + github.com/google/pprof v0.0.0-20260507013755-92041b743c96 ++ github.com/microsoft/go-infra/telemetry v0.0.0-20260526160655-aa04f117b3ce ++ github.com/microsoft/go-infra/telemetry/config v0.0.0-20260526160655-aa04f117b3ce + golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 + golang.org/x/build v0.0.0-20260522210304-d55d0041b921 + golang.org/x/mod v0.36.1-0.20260520130633-087f6515dd3b diff --git a/src/cmd/go.sum b/src/cmd/go.sum -index b8531a556a60ab..4173afb82bbd89 100644 +index 929e1c34dcbec1..e5b03ca050f661 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum -@@ -4,6 +4,10 @@ github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/v - github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +@@ -4,6 +4,10 @@ github.com/google/pprof v0.0.0-20260507013755-92041b743c96 h1:YDDnaZ9afWajDboPMt + github.com/google/pprof v0.0.0-20260507013755-92041b743c96/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA= github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -+github.com/microsoft/go-infra/telemetry v0.0.0-20260513084116-8db604e72b76 h1:nP1tNhLNK4ceYOGVEfAY44tj/5Q0vq/pOqZggUzXHXc= -+github.com/microsoft/go-infra/telemetry v0.0.0-20260513084116-8db604e72b76/go.mod h1:LxxLUDlqi1gwmGrnh1slAFqnEhZnKjn37MABZ6xJs44= -+github.com/microsoft/go-infra/telemetry/config v0.0.0-20260513084116-8db604e72b76 h1:LESU567RNJA+E3w8PyUdwbswh/PY8vF0xvsf3fUERMI= -+github.com/microsoft/go-infra/telemetry/config v0.0.0-20260513084116-8db604e72b76/go.mod h1:t6u8QcO4tExYT4+NEB0XqR0ObiUvLe0D8ZpxFobGuA8= ++github.com/microsoft/go-infra/telemetry v0.0.0-20260526160655-aa04f117b3ce h1:mIjWtu9i00ZMtAmGy0gJYvW3P6bqnGt1s1J8aG87UuQ= ++github.com/microsoft/go-infra/telemetry v0.0.0-20260526160655-aa04f117b3ce/go.mod h1:LxxLUDlqi1gwmGrnh1slAFqnEhZnKjn37MABZ6xJs44= ++github.com/microsoft/go-infra/telemetry/config v0.0.0-20260526160655-aa04f117b3ce h1:+9hsuF75XR8yABS2Oo9yevxvqxxS5+58qPH5rwJW4CM= ++github.com/microsoft/go-infra/telemetry/config v0.0.0-20260526160655-aa04f117b3ce/go.mod h1:t6u8QcO4tExYT4+NEB0XqR0ObiUvLe0D8ZpxFobGuA8= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= - golang.org/x/arch v0.27.1-0.20260513003155-2ebc08890589 h1:vhLfA6kUzRvCYV5uFBJbdMndztX1STqGC8GmQJsuldY= + golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 h1:OEbDVxixMxnrAI3whhcFkCb0rPrEHwxeSSUMdN0V414= diff --git a/src/cmd/internal/telemetry/counter/deps_ignore.go b/src/cmd/internal/telemetry/counter/deps_ignore.go new file mode 100644 index 00000000000000..d3155d34977b32 @@ -619,6 +621,109 @@ index 00000000000000..5fc9356268f64a + +The [`appinsights`](appinsights) package is an alternative client that can send more arbitrary telemetry event data to Application Insights. +It only supports a few features of Application Insights that are used in other projects maintained by the Microsoft build of Go team. +diff --git a/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/ci.go b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/ci.go +new file mode 100644 +index 00000000000000..bb3188f26ead88 +--- /dev/null ++++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/ci.go +@@ -0,0 +1,97 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT License. ++ ++package telemetry ++ ++import "strings" ++ ++// DetectCI inspects the given environment variables to determine which CI ++// system is most likely in use, if any. The env parameter should be in the same ++// format as os.Environ() (i.e. each entry is "KEY=VALUE"). It returns a short ++// identifier matching the go/ci counter values, or "" if no CI system is ++// detected. ++func DetectCI(env []string) string { ++ m := envMap(env) ++ ++ // Azure Pipelines ++ // https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables#system-variables-devops-services ++ if isTrue(m["TF_BUILD"]) { ++ return "azdo" ++ } ++ ++ // GitHub Actions ++ // https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables ++ if isTrue(m["GITHUB_ACTIONS"]) { ++ return "github" ++ } ++ ++ // GitLab CI ++ // https://docs.gitlab.com/ee/ci/variables/predefined_variables.html ++ if m["GITLAB_CI"] != "" { ++ return "gitlab" ++ } ++ ++ // AppVeyor ++ // https://www.appveyor.com/docs/environment-variables/ ++ if isTrue(m["APPVEYOR"]) { ++ return "appveyor" ++ } ++ ++ // Travis CI ++ // https://docs.travis-ci.com/user/environment-variables/#default-environment-variables ++ if isTrue(m["TRAVIS"]) { ++ return "travis" ++ } ++ ++ // CircleCI ++ // https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables ++ if isTrue(m["CIRCLECI"]) { ++ return "circleci" ++ } ++ ++ // AWS CodeBuild ++ // https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html ++ if m["CODEBUILD_BUILD_ID"] != "" && m["AWS_REGION"] != "" { ++ return "aws_codebuild" ++ } ++ ++ // TeamCity ++ // https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html#Predefined+Server+Build+Parameters ++ if m["TEAMCITY_VERSION"] != "" { ++ return "teamcity" ++ } ++ ++ // Jenkins ++ // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/resources/jenkins/model/CoreEnvironmentContributor/buildEnv.groovy ++ if m["BUILD_ID"] != "" && m["BUILD_URL"] != "" { ++ return "jenkins" ++ } ++ ++ // Google Cloud Build ++ // https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values#using_default_substitutions ++ if m["BUILD_ID"] != "" && m["PROJECT_ID"] != "" { ++ return "google_cloud_build" ++ } ++ ++ return "" ++} ++ ++// envMap converts an os.Environ()-style slice into a map for fast lookup. ++func envMap(env []string) map[string]string { ++ m := make(map[string]string, len(env)) ++ for _, e := range env { ++ if k, v, ok := strings.Cut(e, "="); ok { ++ m[k] = v ++ } ++ } ++ return m ++} ++ ++// isTrue reports whether the value is a common boolean-true string. ++// Matches the logic in dotnet/sdk EnvironmentVariableParser.ParseBool. ++func isTrue(v string) bool { ++ return v == "1" || ++ strings.EqualFold(v, "true") || ++ strings.EqualFold(v, "yes") || ++ strings.EqualFold(v, "on") ++} diff --git a/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/LICENSE b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/LICENSE new file mode 100644 index 00000000000000..9e841e7a26e4eb @@ -665,10 +770,10 @@ index 00000000000000..044268a046a54d +var Config []byte diff --git a/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json new file mode 100644 -index 00000000000000..4b16ad97f35d1a +index 00000000000000..e2ba3596943583 --- /dev/null +++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json -@@ -0,0 +1,76 @@ +@@ -0,0 +1,77 @@ +{ + "GOOS": [ + "aix", @@ -721,7 +826,8 @@ index 00000000000000..4b16ad97f35d1a + "Name": "cmd/go", + "Counters": [ + { -+ "Name": "go/invocations" ++ "Name": "go/invocations", ++ "Properties": ["msgo/module/hash"] + }, + { + "Name": "go/goexperiment:{ms_tls_config_schannel,systemcrypto,nosystemcrypto,opensslcrypto,cngcrypto,darwincrypto,ms_nocgo_opensslcrypto}" @@ -730,13 +836,13 @@ index 00000000000000..4b16ad97f35d1a + "Name": "go/subcommand:{build,install,run}" + }, + { -+ "Name": "go/platform/target/goos:{aix,android,darwin,dragonfly,freebsd,hurd,illumos,ios,js,linux,nacl,netbsd,openbsd,plan9,solaris,wasip1,windows,zos}" ++ "Name": "go/platform/target/port:{aix-ppc64,android-386,android-amd64,android-arm,android-arm64,darwin-amd64,darwin-arm64,dragonfly-amd64,freebsd-386,freebsd-amd64,freebsd-arm,freebsd-arm64,freebsd-riscv64,illumos-amd64,ios-amd64,ios-arm64,js-wasm,linux-386,linux-amd64,linux-arm,linux-arm64,linux-loong64,linux-mips,linux-mips64,linux-mips64le,linux-mipsle,linux-ppc64,linux-ppc64le,linux-riscv64,linux-s390x,linux-sparc64,netbsd-386,netbsd-amd64,netbsd-arm,netbsd-arm64,openbsd-386,openbsd-amd64,openbsd-arm,openbsd-arm64,openbsd-ppc64,openbsd-riscv64,plan9-386,plan9-amd64,plan9-arm,solaris-amd64,wasip1-wasm,windows-386,windows-amd64,windows-arm64}" + }, + { -+ "Name": "go/platform/target/goarch:{386,amd64,amd64p32,arm,arm64,arm64be,armbe,loong64,mips,mips64,mips64le,mips64p32,mips64p32le,mipsle,ppc,ppc64,ppc64le,riscv,riscv64,s390,s390x,sparc,sparc64,wasm}" ++ "Name": "go/cgo:{enabled,disabled}" + }, + { -+ "Name": "go/cgo:{enabled,disabled}" ++ "Name": "msgo/ci:{appveyor,aws_codebuild,azdo,circleci,github,gitlab,google_cloud_build,jenkins,teamcity,travis}" + }, + { + "Name": "msgo/systemcrypto:{enabled,disabled}" @@ -748,10 +854,10 @@ index 00000000000000..4b16ad97f35d1a \ No newline at end of file diff --git a/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/counter/counter.go b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/counter/counter.go new file mode 100644 -index 00000000000000..542b9c780b5f1b +index 00000000000000..808caafd45e383 --- /dev/null +++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/counter/counter.go -@@ -0,0 +1,63 @@ +@@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -784,6 +890,14 @@ index 00000000000000..542b9c780b5f1b + return telemetry.Client.NewEvent(name, nil) +} + ++// NewWithProperties returns a counter with the given name and custom properties. ++// Properties are sent as custom dimensions in Application Insights, allowing ++// high-cardinality values (such as hashes) to be sent as values rather than ++// as part of the counter name. ++func NewWithProperties(name string, properties map[string]string) *Counter { ++ return telemetry.Client.NewEvent(name, properties) ++} ++ +// CountFlags creates a counter for every flag that is set +// and increments the counter. The name of the counter is +// the concatenation of prefix and the flag name. @@ -835,10 +949,10 @@ index 00000000000000..2e96890f60b871 +- Improve testing. diff --git a/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/internal/appinsights/client.go b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/internal/appinsights/client.go new file mode 100644 -index 00000000000000..c3dd88924fdcfa +index 00000000000000..b54a026b0fe540 --- /dev/null +++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/internal/appinsights/client.go -@@ -0,0 +1,192 @@ +@@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -894,6 +1008,11 @@ index 00000000000000..c3dd88924fdcfa + // If nil, all telemetry items are sent. + UploadFilter func(name string) bool + ++ // Function to filter property keys for a given event name. ++ // Returns a new map containing only the allowed keys. ++ // If nil, all properties are sent. ++ PropertyFilter func(name string, properties map[string]string) map[string]string ++ + channel *inMemoryChannel + context *telemetryContext + @@ -995,6 +1114,9 @@ index 00000000000000..c3dd88924fdcfa + if n == 0 || (c.UploadFilter != nil && !c.UploadFilter(data.Name)) { + return + } ++ if c.PropertyFilter != nil && data.Properties != nil { ++ data.Properties = c.PropertyFilter(data.Name, data.Properties) ++ } + if !c.init() { + // Stop or Close consumed initOnce before init could run. + return @@ -1897,10 +2019,10 @@ index 00000000000000..0299c87c81cb41 +} diff --git a/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/internal/config/config.go b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/internal/config/config.go new file mode 100644 -index 00000000000000..1cb6c22a7bcd72 +index 00000000000000..e0070ab70977e5 --- /dev/null +++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/internal/config/config.go -@@ -0,0 +1,78 @@ +@@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -1921,16 +2043,18 @@ index 00000000000000..1cb6c22a7bcd72 + +// A ProgramConfig contains the configuration for a single program. +type ProgramConfig struct { -+ // the counter names may have to be -+ // repeated for each program. (e.g., if the counters are in a package -+ // that is used in more than one program.) ++ // The counter and property names may have to be repeated for each ++ // program (e.g., if they are defined in a package used by more than ++ // one program). + Name string + Counters []CounterConfig `json:",omitempty"` +} + +// A CounterConfig contains the configuration for a single counter. ++// Counters may optionally declare associated property event names. +type CounterConfig struct { -+ Name string // The "collapsed" counter: :{,,...} ++ Name string // The "collapsed" counter: :{,,...} ++ Properties []string `json:",omitempty"` // Associated property event names +} + +func ReadConfig(file string) (*UploadConfig, error) { @@ -2071,10 +2195,10 @@ index 00000000000000..02d112edb8ad3e +} diff --git a/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/telemetry.go b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/telemetry.go new file mode 100644 -index 00000000000000..e50deaa4189b18 +index 00000000000000..016de9bdd95956 --- /dev/null +++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/telemetry.go -@@ -0,0 +1,144 @@ +@@ -0,0 +1,168 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -2131,8 +2255,9 @@ index 00000000000000..e50deaa4189b18 + Logger *slog.Logger +} + -+var countersToUpload map[string]struct{} ++var eventsToUpload map[string]struct{} +var wildcardPrefixes []string ++var allowedProperties map[string]map[string]struct{} + +// Start initializes telemetry using the specified configuration. +func Start(cfg Config) { @@ -2168,7 +2293,8 @@ index 00000000000000..e50deaa4189b18 + if progIdx == -1 { + return // Program not configured for telemetry + } -+ countersToUpload = make(map[string]struct{}) ++ eventsToUpload = make(map[string]struct{}) ++ allowedProperties = make(map[string]map[string]struct{}) + wildcardPrefixes = nil + for _, c := range uploadConfig.Programs[progIdx].Counters { + if c.Name == "" { @@ -2180,7 +2306,14 @@ index 00000000000000..e50deaa4189b18 + wildcardPrefixes = append(wildcardPrefixes, prefix) + } + } else { -+ countersToUpload[e] = struct{}{} ++ eventsToUpload[e] = struct{}{} ++ if len(c.Properties) > 0 { ++ keys := make(map[string]struct{}, len(c.Properties)) ++ for _, p := range c.Properties { ++ keys[p] = struct{}{} ++ } ++ allowedProperties[e] = keys ++ } + } + } + } @@ -2194,8 +2327,9 @@ index 00000000000000..e50deaa4189b18 + "ai.application.ver": ver, + "ai.cloud.role": prog, + }, -+ UploadFilter: uploadFilter, -+ Logger: cfg.Logger, ++ UploadFilter: uploadFilter, ++ PropertyFilter: propertyFilter, ++ Logger: cfg.Logger, + }) +} + @@ -2209,7 +2343,7 @@ index 00000000000000..e50deaa4189b18 +} + +func uploadFilter(name string) bool { -+ if _, ok := countersToUpload[name]; ok { ++ if _, ok := eventsToUpload[name]; ok { + return true + } + for _, prefix := range wildcardPrefixes { @@ -2219,15 +2353,29 @@ index 00000000000000..e50deaa4189b18 + } + return false +} ++ ++func propertyFilter(name string, properties map[string]string) map[string]string { ++ keys, ok := allowedProperties[name] ++ if !ok { ++ return nil ++ } ++ filtered := make(map[string]string, len(keys)) ++ for k, v := range properties { ++ if _, ok := keys[k]; ok { ++ filtered[k] = v ++ } ++ } ++ return filtered ++} diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt -index b8a8f795ec2475..0844d267c939ea 100644 +index 4774f1a02e905f..3c77716f1fe9b0 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -16,6 +16,17 @@ github.com/google/pprof/third_party/svgpan # github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b ## explicit; go 1.13 github.com/ianlancetaylor/demangle -+# github.com/microsoft/go-infra/telemetry v0.0.0-20260513084116-8db604e72b76 ++# github.com/microsoft/go-infra/telemetry v0.0.0-20260526160655-aa04f117b3ce +## explicit; go 1.25 +github.com/microsoft/go-infra/telemetry +github.com/microsoft/go-infra/telemetry/counter @@ -2235,10 +2383,10 @@ index b8a8f795ec2475..0844d267c939ea 100644 +github.com/microsoft/go-infra/telemetry/internal/appinsights/internal/contracts +github.com/microsoft/go-infra/telemetry/internal/config +github.com/microsoft/go-infra/telemetry/internal/telemetry -+# github.com/microsoft/go-infra/telemetry/config v0.0.0-20260513084116-8db604e72b76 ++# github.com/microsoft/go-infra/telemetry/config v0.0.0-20260526160655-aa04f117b3ce +## explicit; go 1.24 +github.com/microsoft/go-infra/telemetry/config - # golang.org/x/arch v0.27.1-0.20260513003155-2ebc08890589 + # golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 ## explicit; go 1.25.0 golang.org/x/arch/arm/armasm diff --git a/src/crypto/internal/backend/deps_ignore.go b/src/crypto/internal/backend/deps_ignore.go @@ -2270,35 +2418,35 @@ index 00000000000000..f5dc9afe4b0ead +// that are used by the backend package. This allows to track +// their versions in a single patch file. diff --git a/src/go.mod b/src/go.mod -index 8703fad3b226c9..fe3cd514ef512d 100644 +index bb6abc93792f39..46d8c6ad6bd22d 100644 --- a/src/go.mod +++ b/src/go.mod @@ -11,3 +11,9 @@ require ( - golang.org/x/sys v0.44.0 // indirect + golang.org/x/sys v0.45.0 // indirect golang.org/x/text v0.37.0 // indirect ) + +require ( + github.com/microsoft/go-crypto-darwin v0.0.3-0.20260512212935-d0ce8397e44b -+ github.com/microsoft/go-crypto-openssl v0.0.0-20260521090922-bd6798f35c69 ++ github.com/microsoft/go-crypto-openssl v0.0.0-20260526094617-f3ee9e48499e + github.com/microsoft/go-crypto-winnative v0.0.0-20260512074019-00d811a4aefe +) diff --git a/src/go.sum b/src/go.sum -index 43554ff45c183e..01f4f244d6f7be 100644 +index ab34844da17757..060e1a1e1ae6fc 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,3 +1,9 @@ +github.com/microsoft/go-crypto-darwin v0.0.3-0.20260512212935-d0ce8397e44b h1:HADuteQytnLec1jmAOGPGCukWIxObrAm/CBYtVzuo5o= +github.com/microsoft/go-crypto-darwin v0.0.3-0.20260512212935-d0ce8397e44b/go.mod h1:QahyqOoEDhEJ08aC1WtiWq691LyNgXq3qrjI4QmdPzM= -+github.com/microsoft/go-crypto-openssl v0.0.0-20260521090922-bd6798f35c69 h1:gtZRTEEfTbjAKMNbDGXeayGoReqVhBIw+J8QzgWuBEs= -+github.com/microsoft/go-crypto-openssl v0.0.0-20260521090922-bd6798f35c69/go.mod h1:gJrjX+yWGi9pkbfPVDDh+ZbgjtQoRSXHjb/ZyjwKk34= ++github.com/microsoft/go-crypto-openssl v0.0.0-20260526094617-f3ee9e48499e h1:969cC0h4W/zAzgFu09YW/6MwRkEDTAVlDB1CKiGG+Qs= ++github.com/microsoft/go-crypto-openssl v0.0.0-20260526094617-f3ee9e48499e/go.mod h1:gJrjX+yWGi9pkbfPVDDh+ZbgjtQoRSXHjb/ZyjwKk34= +github.com/microsoft/go-crypto-winnative v0.0.0-20260512074019-00d811a4aefe h1:WDbUuTTKY8VElMRA3JJ8Quvj9xK9GqEcTiBZ3BF+7mI= +github.com/microsoft/go-crypto-winnative v0.0.0-20260512074019-00d811a4aefe/go.mod h1:a1Z07CJIuWa8WT/pzFIGNTTKS96s8o1B1TPOziAHUxw= - golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= - golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= - golang.org/x/net v0.54.1-0.20260509022215-ad8140e0aa2e h1:ITxjUIeFp/ACbvnNlOZLoDmomcET3Yt4lFMgeqBebXk= + golang.org/x/crypto v0.52.1-0.20260526024921-9beb694f9766 h1:ABD+jVg0H4Hwu2sGcUtKeb3T8mlS+jS3uWrkTAPcXjs= + golang.org/x/crypto v0.52.1-0.20260526024921-9beb694f9766/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= + golang.org/x/net v0.55.1-0.20260526154343-657eb1317b5d h1:G6GZDsxGyGK2SxMEqnPJfBWRKGCNpWheup5btZYkYpw= diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index 25cb06182cae3d..02b52e2ab9c296 100644 +index 25cb06182cae3d..920e72e5cdbb9d 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -153,6 +153,13 @@ var depsRules = ` @@ -2397,7 +2545,7 @@ index 25cb06182cae3d..02b52e2ab9c296 100644 } fset := token.NewFileSet() diff --git a/src/go/build/vendor_test.go b/src/go/build/vendor_test.go -index 7f6237ffd59c11..8f68bc361081f1 100644 +index 7f6237ffd59c11..18a3b42927d800 100644 --- a/src/go/build/vendor_test.go +++ b/src/go/build/vendor_test.go @@ -22,6 +22,10 @@ var allowedPackagePrefixes = []string{ @@ -26597,10 +26745,10 @@ index 00000000000000..410829738931c7 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/cipher.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/cipher.go new file mode 100644 -index 00000000000000..24615432053f01 +index 00000000000000..10f932a02d0b36 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/cipher.go -@@ -0,0 +1,666 @@ +@@ -0,0 +1,665 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -26687,8 +26835,7 @@ index 00000000000000..24615432053f01 + } + defer func() { + if cipher != nil { -+ switch major() { -+ case 3, 4: ++ if major() != 1 { + // On OpenSSL 3, directly operating on a EVP_CIPHER object + // not created by EVP_CIPHER has negative performance + // implications, as cipher operations will have @@ -27773,10 +27920,10 @@ index 00000000000000..6ae4a6372f8d08 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/dsa.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/dsa.go new file mode 100644 -index 00000000000000..82b7b1081468de +index 00000000000000..b2c643ab34a0c1 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/dsa.go -@@ -0,0 +1,306 @@ +@@ -0,0 +1,296 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -27872,7 +28019,7 @@ index 00000000000000..82b7b1081468de + case 1: + dsa := getDSA(pkey) + ossl.DSA_get0_pqg(dsa, &p, &q, &g) -+ case 3, 4: ++ default: + defer func() { + ossl.BN_free(p) + ossl.BN_free(q) @@ -27887,8 +28034,6 @@ index 00000000000000..82b7b1081468de + if _, err := ossl.EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_FFC_G.ptr(), &g); err != nil { + return DSAParameters{}, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + + return DSAParameters{ @@ -27938,7 +28083,7 @@ index 00000000000000..82b7b1081468de + case 1: + dsa := getDSA(pkey) + ossl.DSA_get0_key(dsa, &by, &bx) -+ case 3, 4: ++ default: + defer func() { + ossl.BN_clear_free(bx) + ossl.BN_free(by) @@ -27949,8 +28094,6 @@ index 00000000000000..82b7b1081468de + if _, err := ossl.EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY.ptr(), &bx); err != nil { + return nil, nil, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + return bnToBig(bx), bnToBig(by), nil +} @@ -27969,16 +28112,12 @@ index 00000000000000..82b7b1081468de + switch major() { + case 1: + return newDSA1(params, x, y) -+ case 3, 4: -+ return newDSA3(params, x, y) + default: -+ panic(errUnsupportedVersion()) ++ return newDSA3(params, x, y) + } +} + +func newDSA1(params DSAParameters, x, y BigInt) (pkey ossl.EVP_PKEY_PTR, err error) { -+ checkMajorVersion(1) -+ + dsa, err := ossl.DSA_new() + if err != nil { + return nil, err @@ -28024,8 +28163,6 @@ index 00000000000000..82b7b1081468de +} + +func newDSA3(params DSAParameters, x, y BigInt) (ossl.EVP_PKEY_PTR, error) { -+ checkMajorVersion(3, 4) -+ + bld := newParamBuilder() + defer bld.finalize() + @@ -28223,10 +28360,10 @@ index 00000000000000..d871a04b37942b +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ecdh.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ecdh.go new file mode 100644 -index 00000000000000..7289538d753dc8 +index 00000000000000..60e56dfd573f47 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ecdh.go -@@ -0,0 +1,341 @@ +@@ -0,0 +1,331 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -28336,7 +28473,7 @@ index 00000000000000..7289538d753dc8 + if bytes, err = encodeEcPoint(group, pt); err != nil { + return nil, err + } -+ case 3, 4: ++ default: + pkey = k._pkey + if _, err := ossl.EVP_PKEY_up_ref(pkey); err != nil { + return nil, err @@ -28348,8 +28485,6 @@ index 00000000000000..7289538d753dc8 + } + bytes = goBytes(unsafe.Pointer(cbytes), n) + cryptoFree(unsafe.Pointer(cbytes)) -+ default: -+ panic(errUnsupportedVersion()) + } + } + pub := &PublicKeyECDH{pkey, bytes} @@ -28370,16 +28505,12 @@ index 00000000000000..7289538d753dc8 + switch major() { + case 1: + return newECDHPkey1(nid, bytes, isPrivate) -+ case 3, 4: -+ return newECDHPkey3(nid, bytes, isPrivate) + default: -+ panic(errUnsupportedVersion()) ++ return newECDHPkey3(nid, bytes, isPrivate) + } +} + +func newECDHPkey1(nid int32, bytes []byte, isPrivate bool) (pkey ossl.EVP_PKEY_PTR, err error) { -+ checkMajorVersion(1) -+ + key, err := ossl.EC_KEY_new_by_curve_name(nid) + if err != nil { + return nil, err @@ -28432,8 +28563,6 @@ index 00000000000000..7289538d753dc8 +} + +func newECDHPkey3(nid int32, bytes []byte, isPrivate bool) (ossl.EVP_PKEY_PTR, error) { -+ checkMajorVersion(3, 4) -+ + bld := newParamBuilder() + defer bld.finalize() + bld.addUTF8String(_OSSL_PKEY_PARAM_GROUP_NAME, ossl.OBJ_nid2sn(nid), 0) @@ -28542,13 +28671,11 @@ index 00000000000000..7289538d753dc8 + if priv == nil { + return nil, nil, fail("missing ECDH private key") + } -+ case 3, 4: ++ default: + if _, err := ossl.EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_PRIV_KEY.ptr(), &priv); err != nil { + return nil, nil, err + } + defer ossl.BN_clear_free(priv) -+ default: -+ panic(errUnsupportedVersion()) + } + // We should not leak bit length of the secret scalar in the key. + // For this reason, we use BN_bn2binpad instead of BN_bn2bin with fixed length. @@ -28570,10 +28697,10 @@ index 00000000000000..7289538d753dc8 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ecdsa.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ecdsa.go new file mode 100644 -index 00000000000000..d96c368fe1ca76 +index 00000000000000..ad6c0bcb821bc7 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ecdsa.go -@@ -0,0 +1,220 @@ +@@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -28668,7 +28795,7 @@ index 00000000000000..d96c368fe1ca76 + } + // Get Z. We don't need to free it, get0 does not increase the reference count. + bd = ossl.EC_KEY_get0_private_key(key) -+ case 3, 4: ++ default: + if _, err := ossl.EVP_PKEY_get_bn_param(pkey, _OSSL_PKEY_PARAM_EC_PUB_X.ptr(), &bx); err != nil { + return nil, nil, nil, err + } @@ -28679,8 +28806,6 @@ index 00000000000000..d96c368fe1ca76 + return nil, nil, nil, err + } + defer ossl.BN_clear_free(bd) -+ default: -+ panic(errUnsupportedVersion()) + } + + // Get D. @@ -28726,16 +28851,12 @@ index 00000000000000..d96c368fe1ca76 + switch major() { + case 1: + return newECDSAKey1(nid, bx, by, bd) -+ case 3, 4: -+ return newECDSAKey3(nid, bx, by, bd) + default: -+ panic(errUnsupportedVersion()) ++ return newECDSAKey3(nid, bx, by, bd) + } +} + +func newECDSAKey1(nid int32, bx, by, bd ossl.BIGNUM_PTR) (pkey ossl.EVP_PKEY_PTR, err error) { -+ checkMajorVersion(1) -+ + key, err := ossl.EC_KEY_new_by_curve_name(nid) + if err != nil { + return nil, err @@ -28758,8 +28879,6 @@ index 00000000000000..d96c368fe1ca76 +} + +func newECDSAKey3(nid int32, bx, by, bd ossl.BIGNUM_PTR) (ossl.EVP_PKEY_PTR, error) { -+ checkMajorVersion(3, 4) -+ + // Create the encoded public key public key from bx and by. + pubBytes, err := generateAndEncodeEcPublicKey(nid, func(group ossl.EC_GROUP_PTR) (ossl.EC_POINT_PTR, error) { + pt, err := ossl.EC_POINT_new(group) @@ -28796,7 +28915,7 @@ index 00000000000000..d96c368fe1ca76 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ed25519.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ed25519.go new file mode 100644 -index 00000000000000..205b01cd36b206 +index 00000000000000..69565f949d4ac5 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/ed25519.go @@ -0,0 +1,209 @@ @@ -28836,7 +28955,7 @@ index 00000000000000..205b01cd36b206 + ossl.EVP_PKEY_CTX_free(ctx) + return true + } -+ case 3, 4: ++ default: + sig, _ := ossl.EVP_SIGNATURE_fetch(nil, _KeyTypeED25519.ptr(), nil) + if sig != nil { + ossl.EVP_SIGNATURE_free(sig) @@ -29011,10 +29130,10 @@ index 00000000000000..205b01cd36b206 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/evp.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/evp.go new file mode 100644 -index 00000000000000..65f4d2ddcbc6fb +index 00000000000000..3752d3ce4d18bd --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/evp.go -@@ -0,0 +1,615 @@ +@@ -0,0 +1,616 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -29176,8 +29295,7 @@ index 00000000000000..65f4d2ddcbc6fb + hash.ch = ch + hash.size = int(ossl.EVP_MD_get_size(hash.md)) + hash.blockSize = int(ossl.EVP_MD_get_block_size(hash.md)) -+ switch major() { -+ case 3, 4: ++ if major() != 1 { + // On OpenSSL 3, directly operating on a EVP_MD object + // not created by EVP_MD_fetch has negative performance + // implications, as digest operations will have @@ -29198,23 +29316,27 @@ index 00000000000000..65f4d2ddcbc6fb + switch major() { + case 1: + hash.provider = providerOSSLDefault -+ case 3, 4: ++ default: + if prov := ossl.EVP_MD_get0_provider(hash.md); prov != nil { + cname := ossl.OSSL_PROVIDER_get0_name(prov) ++ // Marshalability depends on knowing the EVP_MD_CTX internal ++ // layout for this major (see getOSSLDigetsContext). Untested ++ // majors loaded via GODEBUG=ms_opensslallowuntested=1 leave ++ // marshallable false so MarshalBinary/UnmarshalBinary return ++ // errMarshallUnsupported{} instead of touching unknown memory. ++ known := knownMajor() + switch goString(cname) { + case "default": + hash.provider = providerOSSLDefault -+ hash.marshallable = hash.magic != "" ++ hash.marshallable = known && hash.magic != "" + case "fips": + hash.provider = providerOSSLFIPS -+ hash.marshallable = hash.magic != "" ++ hash.marshallable = known && hash.magic != "" + case "symcryptprovider": + hash.provider = providerSymCrypt -+ hash.marshallable = hash.magic != "" && isSymCryptHashStateSerializable(hash.md) ++ hash.marshallable = known && hash.magic != "" && isSymCryptHashStateSerializable(hash.md) + } + } -+ default: -+ panic(errUnsupportedVersion()) + } + + cacheMD.Store(ch, &hash) @@ -29250,7 +29372,7 @@ index 00000000000000..65f4d2ddcbc6fb + if _, err := ossl.EVP_PKEY_keygen(ctx, &pkey); err != nil { + return nil, err + } -+ case 3, 4: ++ default: + var err error + switch id { + case ossl.EVP_PKEY_RSA: @@ -29277,8 +29399,6 @@ index 00000000000000..65f4d2ddcbc6fb + if err != nil { + return nil, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + + return pkey, nil @@ -29403,10 +29523,10 @@ index 00000000000000..65f4d2ddcbc6fb + copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label) + var err error + switch major() { -+ case 3, 4: -+ _, err = ossl.EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, unsafe.Slice(clabel, len(label))) -+ default: ++ case 1: + _, err = ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_OAEP_LABEL, int32(len(label)), unsafe.Pointer(clabel)) ++ default: ++ _, err = ossl.EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, unsafe.Slice(clabel, len(label))) + } + if err != nil { + cryptoFree(unsafe.Pointer(clabel)) @@ -30159,10 +30279,10 @@ index 00000000000000..020323eb7be25e +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/hkdf.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/hkdf.go new file mode 100644 -index 00000000000000..75207faf1aa043 +index 00000000000000..6ae76c729a7aa0 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/hkdf.go -@@ -0,0 +1,330 @@ +@@ -0,0 +1,312 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -30181,11 +30301,9 @@ index 00000000000000..75207faf1aa043 + switch major() { + case 1: + return true -+ case 3, 4: ++ default: + _, err := fetchHKDF3() + return err == nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -30194,18 +30312,14 @@ index 00000000000000..75207faf1aa043 + switch major() { + case 1: + return false -+ case 3, 4: ++ default: + // TLS13-KDF is available in OpenSSL 3.0.0 and later. + _, err := fetchTLS13_KDF() + return err == nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + +func newHKDFCtx1(md ossl.EVP_MD_PTR, mode int32, secret, salt, pseudorandomKey, info []byte) (ctx ossl.EVP_PKEY_CTX_PTR, err error) { -+ checkMajorVersion(1) -+ + ctx, err = ossl.EVP_PKEY_CTX_new_id(ossl.EVP_PKEY_HKDF, nil) + if err != nil { + return nil, err @@ -30294,7 +30408,7 @@ index 00000000000000..75207faf1aa043 + return nil, err + } + return out[:keylen], nil -+ case 3, 4: ++ default: + ctx, err := newHKDFCtx3(md, ossl.EVP_KDF_HKDF_MODE_EXTRACT_ONLY, secret, salt, nil, nil) + if err != nil { + return nil, err @@ -30309,8 +30423,6 @@ index 00000000000000..75207faf1aa043 + return nil, err + } + return out, nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -30343,7 +30455,7 @@ index 00000000000000..75207faf1aa043 + if _, err := ossl.EVP_PKEY_derive(ctx, out, &keylen); err != nil { + return nil, err + } -+ case 3, 4: ++ default: + ctx, err := newHKDFCtx3(md, ossl.EVP_KDF_HKDF_MODE_EXPAND_ONLY, nil, nil, pseudorandomKey, info) + if err != nil { + return nil, err @@ -30358,8 +30470,6 @@ index 00000000000000..75207faf1aa043 + if _, err := ossl.EVP_KDF_derive(ctx, out, nil); err != nil { + return nil, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + return out, nil +} @@ -30393,8 +30503,6 @@ index 00000000000000..75207faf1aa043 +// It is safe to call this function concurrently. +// The returned EVP_KDF_PTR shouldn't be freed. +var fetchTLS13_KDF = sync.OnceValues(func() (ossl.EVP_KDF_PTR, error) { -+ checkMajorVersion(3, 4) -+ + kdf, err := ossl.EVP_KDF_fetch(nil, _OSSL_KDF_NAME_TLS13_KDF.ptr(), nil) + if err != nil { + return nil, err @@ -30404,8 +30512,6 @@ index 00000000000000..75207faf1aa043 + +// newTLS13KDFExpandCtx3 fetches the "TLS13-KDF" for TLS 1.3 handshakes. +func newTLS13KDFExpandCtx3(md ossl.EVP_MD_PTR, label, context, pseudorandomKey []byte) (_ ossl.EVP_KDF_CTX_PTR, err error) { -+ checkMajorVersion(3, 4) -+ + kdf, err := fetchTLS13_KDF() + if err != nil { + return nil, err @@ -30446,8 +30552,6 @@ index 00000000000000..75207faf1aa043 +// It is safe to call this function concurrently. +// The returned EVP_KDF_PTR shouldn't be freed. +var fetchHKDF3 = sync.OnceValues(func() (ossl.EVP_KDF_PTR, error) { -+ checkMajorVersion(3, 4) -+ + kdf, err := ossl.EVP_KDF_fetch(nil, _OSSL_KDF_NAME_HKDF.ptr(), nil) + if err != nil { + return nil, err @@ -30457,8 +30561,6 @@ index 00000000000000..75207faf1aa043 + +// newHKDFCtx3 implements HKDF for OpenSSL 3 using the EVP_KDF API. +func newHKDFCtx3(md ossl.EVP_MD_PTR, mode int32, secret, salt, pseudorandomKey, info []byte) (_ ossl.EVP_KDF_CTX_PTR, err error) { -+ checkMajorVersion(3, 4) -+ + kdf, err := fetchHKDF3() + if err != nil { + return nil, err @@ -30495,10 +30597,10 @@ index 00000000000000..75207faf1aa043 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/hmac.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/hmac.go new file mode 100644 -index 00000000000000..88f6ce7eb85ca1 +index 00000000000000..6402a9b3d0f30a --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/hmac.go -@@ -0,0 +1,280 @@ +@@ -0,0 +1,269 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -30545,14 +30647,12 @@ index 00000000000000..88f6ce7eb85ca1 + return nil + } + hmac.ctx1 = ctx -+ case 3, 4: ++ default: + ctx := newHMAC3(key, md) + if ctx.ctx == nil { + return nil + } + hmac.ctx3 = ctx -+ default: -+ panic(errUnsupportedVersion()) + } + runtime.SetFinalizer(hmac, (*opensslHMAC).finalize) + return hmac @@ -30670,12 +30770,10 @@ index 00000000000000..88f6ce7eb85ca1 + if _, err := ossl.HMAC_Init_ex(h.ctx1.ctx, nil, nil, nil); err != nil { + panic(err) + } -+ case 3, 4: ++ default: + if _, err := ossl.EVP_MAC_init(h.ctx3.ctx, h.ctx3.key, nil); err != nil { + panic(err) + } -+ default: -+ panic(errUnsupportedVersion()) + } + + runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure. @@ -30695,10 +30793,8 @@ index 00000000000000..88f6ce7eb85ca1 + switch major() { + case 1: + ossl.HMAC_Update(h.ctx1.ctx, p) -+ case 3, 4: -+ ossl.EVP_MAC_update(h.ctx3.ctx, p) + default: -+ panic(errUnsupportedVersion()) ++ ossl.EVP_MAC_update(h.ctx3.ctx, p) + } + } + runtime.KeepAlive(h) @@ -30729,15 +30825,13 @@ index 00000000000000..88f6ce7eb85ca1 + panic(err) + } + ossl.HMAC_Final(ctx2, h.sum[:h.size], nil) -+ case 3, 4: ++ default: + ctx2, err := ossl.EVP_MAC_CTX_dup(h.ctx3.ctx) + if err != nil { + panic(err) + } + defer ossl.EVP_MAC_CTX_free(ctx2) + ossl.EVP_MAC_final(ctx2, h.sum[:h.size], nil) -+ default: -+ panic(errUnsupportedVersion()) + } + return append(in, h.sum[:h.size]...) +} @@ -30761,7 +30855,7 @@ index 00000000000000..88f6ce7eb85ca1 + runtime.SetFinalizer(cl, (*opensslHMAC).finalize) + return cl, nil + -+ case 3, 4: ++ default: + ctx2, err := ossl.EVP_MAC_CTX_dup(h.ctx3.ctx) + if err != nil { + panic(err) @@ -30774,9 +30868,6 @@ index 00000000000000..88f6ce7eb85ca1 + } + runtime.SetFinalizer(cl, (*opensslHMAC).finalize) + return cl, nil -+ -+ default: -+ panic(errUnsupportedVersion()) + } +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/mldsa.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/mldsa.go @@ -31611,10 +31702,10 @@ index 00000000000000..5c2ffbabe92e15 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/openssl.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/openssl.go new file mode 100644 -index 00000000000000..94fd563ac87978 +index 00000000000000..e0291c15a18c00 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/openssl.go -@@ -0,0 +1,183 @@ +@@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -31658,6 +31749,15 @@ index 00000000000000..94fd563ac87978 + return osslsetup.VersionPatch() +} + ++// knownMajor reports whether the loaded OpenSSL major is one this ++// backend has been tested against. Untested majors are only reachable ++// behind GODEBUG=ms_opensslallowuntested=1; code paths that rely on ++// version-specific layouts (e.g. EVP_MD_CTX internals) must guard on ++// this so they degrade safely instead of touching unknown memory. ++func knownMajor() bool { ++ return osslsetup.IsTestedMajor(major()) ++} ++ +func utoa(n int) string { + return strconv.FormatUint(uint64(n), 10) +} @@ -31666,16 +31766,6 @@ index 00000000000000..94fd563ac87978 + return errors.New("openssl: unsupported OpenSSL version: " + utoa(major()) + "." + utoa(minor()) + "." + utoa(patch()) + " (minimum supported version is 1.1.1)") +} + -+// checkMajorVersion panics if the current major version is not one of the expected versions. -+func checkMajorVersion(expected ...int) { -+ for _, v := range expected { -+ if major() == v { -+ return -+ } -+ } -+ panic("openssl: incorrect major version (" + strconv.Itoa(major()) + ")") -+} -+ +type fail string + +func (e fail) Error() string { return "openssl: " + string(e) + " failed" } @@ -32061,10 +32151,10 @@ index 00000000000000..8bd651d6493707 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/pbkdf2.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/pbkdf2.go new file mode 100644 -index 00000000000000..66af2060e2cf87 +index 00000000000000..6cba126ef9dee4 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/pbkdf2.go -@@ -0,0 +1,86 @@ +@@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -32083,11 +32173,9 @@ index 00000000000000..66af2060e2cf87 + switch major() { + case 1: + return true -+ case 3, 4: ++ default: + _, err := fetchPBKDF2() + return err == nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -32095,8 +32183,6 @@ index 00000000000000..66af2060e2cf87 +// It is safe to call this function concurrently. +// The returned EVP_KDF_PTR shouldn't be freed. +var fetchPBKDF2 = sync.OnceValues(func() (ossl.EVP_KDF_PTR, error) { -+ checkMajorVersion(3, 4) -+ + kdf, err := ossl.EVP_KDF_fetch(nil, _OSSL_KDF_NAME_PBKDF2.ptr(), nil) + if err != nil { + return nil, err @@ -32153,10 +32239,10 @@ index 00000000000000..66af2060e2cf87 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/provideropenssl.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/provideropenssl.go new file mode 100644 -index 00000000000000..f6a4b1ac20b654 +index 00000000000000..7c31310ff8a9ba --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/provideropenssl.go -@@ -0,0 +1,250 @@ +@@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -32354,7 +32440,14 @@ index 00000000000000..f6a4b1ac20b654 + } + return (*mdCtx)(unsafe.Pointer(ctx)).algctx + default: -+ panic(errUnsupportedVersion()) ++ // Unknown OpenSSL major: the EVP_MD_CTX internal layout is not ++ // known, so the running hash state cannot be safely extracted. ++ // loadHash marks hashes as not marshallable on untested majors ++ // (see evp.go), so MarshalBinary/UnmarshalBinary short-circuit ++ // with errMarshallUnsupported{} before calling this. The nil ++ // return is defense in depth against any future caller that ++ // bypasses that gate. ++ return nil + } +} + @@ -32871,10 +32964,10 @@ index 00000000000000..37d12ec889a91e +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/rsa.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/rsa.go new file mode 100644 -index 00000000000000..36f84902a21ece +index 00000000000000..a59ac6f76a1298 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/rsa.go -@@ -0,0 +1,712 @@ +@@ -0,0 +1,706 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -32915,7 +33008,7 @@ index 00000000000000..36f84902a21ece + N, E, D = bnToBig(n), bnToBig(e), bnToBig(d) + P, Q = bnToBig(p), bnToBig(q) + Dp, Dq, Qinv = bnToBig(dmp1), bnToBig(dmq1), bnToBig(iqmp) -+ case 3, 4: ++ default: + tmp, err := ossl.BN_new() + if err != nil { + return bad(err) @@ -32944,8 +33037,6 @@ index 00000000000000..36f84902a21ece + setBigInt(&Qinv, _OSSL_PKEY_PARAM_RSA_COEFFICIENT1)) { + return bad(err) + } -+ default: -+ panic(errUnsupportedVersion()) + } + return +} @@ -32983,13 +33074,11 @@ index 00000000000000..36f84902a21ece + ossl.EVP_PKEY_free(pkey) + return nil, err + } -+ case 3, 4: ++ default: + var err error + if pkey, err = newRSAKey3(false, n, e, nil, nil, nil, nil, nil, nil); err != nil { + return nil, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + k := &PublicKeyRSA{_pkey: pkey} + runtime.SetFinalizer(k, (*PublicKeyRSA).finalize) @@ -33062,13 +33151,11 @@ index 00000000000000..36f84902a21ece + ossl.EVP_PKEY_free(pkey) + return nil, err + } -+ case 3, 4: ++ default: + var err error + if pkey, err = newRSAKey3(true, n, e, d, p, q, dp, dq, qinv); err != nil { + return nil, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + k := &PrivateKeyRSA{_pkey: pkey} + runtime.SetFinalizer(k, (*PrivateKeyRSA).finalize) @@ -33589,10 +33676,10 @@ index 00000000000000..36f84902a21ece +}) diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/tls1prf.go b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/tls1prf.go new file mode 100644 -index 00000000000000..87fe3879ab48d3 +index 00000000000000..d53f18c9fbc24c --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/openssl/tls1prf.go -@@ -0,0 +1,157 @@ +@@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -33612,11 +33699,9 @@ index 00000000000000..87fe3879ab48d3 + switch major() { + case 1: + return minor() >= 1 -+ case 3, 4: ++ default: + _, err := fetchTLS1PRF3() + return err == nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -33646,17 +33731,13 @@ index 00000000000000..87fe3879ab48d3 + switch major() { + case 1: + return tls1PRF1(result, secret, label, seed, md) -+ case 3, 4: -+ return tls1PRF3(result, secret, label, seed, md) + default: -+ return errUnsupportedVersion() ++ return tls1PRF3(result, secret, label, seed, md) + } +} + +// tls1PRF1 implements TLS1PRF for OpenSSL 1 using the EVP_PKEY API. +func tls1PRF1(result, secret, label, seed []byte, md ossl.EVP_MD_PTR) error { -+ checkMajorVersion(1) -+ + ctx, err := ossl.EVP_PKEY_CTX_new_id(ossl.EVP_PKEY_TLS1_PRF, nil) + if err != nil { + return err @@ -33710,8 +33791,6 @@ index 00000000000000..87fe3879ab48d3 +// It is safe to call this function concurrently. +// The returned EVP_KDF_PTR shouldn't be freed. +var fetchTLS1PRF3 = sync.OnceValues(func() (ossl.EVP_KDF_PTR, error) { -+ checkMajorVersion(3, 4) -+ + kdf, err := ossl.EVP_KDF_fetch(nil, _OSSL_KDF_NAME_TLS1_PRF.ptr(), nil) + if err != nil { + return nil, err @@ -33721,8 +33800,6 @@ index 00000000000000..87fe3879ab48d3 + +// tls1PRF3 implements TLS1PRF for OpenSSL 3 using the EVP_KDF API. +func tls1PRF3(result, secret, label, seed []byte, md ossl.EVP_MD_PTR) error { -+ checkMajorVersion(3, 4) -+ + kdf, err := fetchTLS1PRF3() + if err != nil { + return err @@ -33845,10 +33922,10 @@ index 00000000000000..ac79a864271eee +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/fips.go b/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/fips.go new file mode 100644 -index 00000000000000..2084b384b9f3a9 +index 00000000000000..38e4b34472dd55 --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/fips.go -@@ -0,0 +1,69 @@ +@@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -33883,20 +33960,59 @@ index 00000000000000..2084b384b9f3a9 +} + +const ( -+ _ProviderNameFips cString = "fips\x00" ++ // Provider names ++ _ProviderNameFips cString = "fips\x00" ++ ++ // Digest Names + _DigestNameSHA2_256 cString = "SHA2-256\x00" +) + -+// FIPS returns true if the provider used by default matches the `fips=yes` query. -+// It is useful for checking whether OpenSSL is capable of running in FIPS mode regardless -+// of whether the `fips=yes` query is explicitly enabled. For example, Azure Linux 3 doesn't set the -+// `fips=yes` query in the default properties, but sets the default provider to be SCOSSL, -+// which is FIPS-capable. ++// FIPS returns true if OpenSSL is running in FIPS mode and there is ++// a provider available that supports FIPS. It returns false otherwise. ++// All OpenSSL functions used in here should be tagged with "init_1" or "init_3" in shims.h. +func FIPS() bool { + switch vMajor { + case 1: + return ossl.FIPS_mode() == 1 ++ case 3, 4: ++ // Check if the default properties contain `fips=1`. ++ if ossl.EVP_default_properties_is_fips_enabled(nil) != 1 { ++ // Note that it is still possible that the provider used by default is FIPS-compliant, ++ // but that wouldn't be a system or user requirement. ++ return false ++ } ++ // Check if the SHA-256 algorithm is available. If it is, then we can be sure that there is a provider available that matches ++ // the `fips=1` query. Most notably, this works for the common case of using the built-in FIPS provider. ++ // ++ // Note that this approach has a small chance of false negative if the FIPS provider doesn't provide the SHA-256 algorithm, ++ // but that is highly unlikely because SHA-256 is one of the most common algorithms and fundamental to many cryptographic operations. ++ // It also has a small chance of false positive if the FIPS provider implements the SHA-256 algorithm but not the other algorithms ++ // used by the caller application, but that is also unlikely because the FIPS provider should provide all common algorithms. ++ return proveSHA256("") + default: ++ panic(errUnsupportedVersion()) ++ } ++} ++ ++// FIPSCapable returns true if the provider used by default matches the `fips=yes` query. ++// It is useful for checking whether OpenSSL is capable of running in FIPS mode regardless ++// of whether FIPS mode is explicitly enabled. For example, Azure Linux 3 doesn't set the ++// `fips=yes` query in the default properties, but sets the default provider to be SCOSSL, ++// which is FIPS-capable. ++// ++// Considerations: ++// - Can return true even if [FIPS] returns false, because [FIPS] also checks whether ++// the default properties contain `fips=yes`. ++// - When using OpenSSL 3, will always return true if [FIPS] returns true. ++// - When using OpenSSL 1, will always return the same value as [FIPS]. ++// - OpenSSL 3 doesn't provide a way to know if a provider is FIPS-capable. This function uses ++// some heuristics that should be treated as an implementation detail that may change in the future. ++func FIPSCapable() bool { ++ if FIPS() { ++ return true ++ } ++ switch vMajor { ++ case 3, 4: + // Load the provider with and without the `fips=yes` query. + // If the providers are the same, then the default provider is FIPS-capable. + provFIPS := sha256Provider(_ProviderNameFips) @@ -33906,6 +34022,7 @@ index 00000000000000..2084b384b9f3a9 + provDefault := sha256Provider("") + return provFIPS == provDefault + } ++ return false +} + +// sha256Provider returns the provider for the SHA-256 algorithm @@ -33918,6 +34035,12 @@ index 00000000000000..2084b384b9f3a9 + defer ossl.EVP_MD_free(md) + return ossl.EVP_MD_get0_provider(md) +} ++ ++// proveSHA256 checks if the SHA-256 algorithm is available ++// using the given properties. ++func proveSHA256(props cString) bool { ++ return sha256Provider(props) != nil ++} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/init.go b/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/init.go new file mode 100644 index 00000000000000..720a446838ea7d @@ -34220,10 +34343,10 @@ index 00000000000000..312dfc88d7f311 +} diff --git a/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/osslsetup.go b/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/osslsetup.go new file mode 100644 -index 00000000000000..189335d93c80f8 +index 00000000000000..ed33d8efe1edbb --- /dev/null +++ b/src/vendor/github.com/microsoft/go-crypto-openssl/osslsetup/osslsetup.go -@@ -0,0 +1,109 @@ +@@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + @@ -34249,6 +34372,20 @@ index 00000000000000..189335d93c80f8 +// [openLibrary]. +var testedMajors = [...]int{1, 3, 4} + ++// IsTestedMajor reports whether m is one of the OpenSSL major versions ++// this backend has been tested against (see [testedMajors]). Callers ++// that depend on version-specific layouts or behaviors should gate on ++// this so they degrade safely on untested majors loaded via ++// GODEBUG=ms_opensslallowuntested=1. ++func IsTestedMajor(m int) bool { ++ for _, v := range testedMajors { ++ if v == m { ++ return true ++ } ++ } ++ return false ++} ++ +// allowUntestedMajor reports whether the user has set +// GODEBUG=ms_opensslallowuntested=1. The "ms_" prefix marks this as a +// Microsoft-defined GODEBUG so it will not collide with upstream Go. @@ -39833,7 +39970,7 @@ index 00000000000000..1722410e5af193 + return getSystemDirectory() + "\\" + dll +} diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt -index d72017d2a43c1e..277d1992ac034f 100644 +index 54fcbab6a221c0..07fcd41c1e259f 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -1,3 +1,26 @@ @@ -39846,7 +39983,7 @@ index d72017d2a43c1e..277d1992ac034f 100644 +github.com/microsoft/go-crypto-darwin/internal/security +github.com/microsoft/go-crypto-darwin/internal/xsyscall +github.com/microsoft/go-crypto-darwin/xcrypto -+# github.com/microsoft/go-crypto-openssl v0.0.0-20260521090922-bd6798f35c69 ++# github.com/microsoft/go-crypto-openssl v0.0.0-20260526094617-f3ee9e48499e +## explicit; go 1.25 +github.com/microsoft/go-crypto-openssl/bbig +github.com/microsoft/go-crypto-openssl/internal/fakecgo @@ -39860,6 +39997,6 @@ index d72017d2a43c1e..277d1992ac034f 100644 +github.com/microsoft/go-crypto-winnative/internal/bcrypt +github.com/microsoft/go-crypto-winnative/internal/subtle +github.com/microsoft/go-crypto-winnative/internal/sysdll - # golang.org/x/crypto v0.51.0 + # golang.org/x/crypto v0.52.1-0.20260526024921-9beb694f9766 ## explicit; go 1.25.0 golang.org/x/crypto/chacha20 diff --git a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch deleted file mode 100644 index 2132d881236..00000000000 --- a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch +++ /dev/null @@ -1,543 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: bot-for-go[bot] <199222863+bot-for-go[bot]@users.noreply.github.com> -Date: Wed, 31 May 2023 16:54:31 -0500 -Subject: [PATCH] Add crypto backend GOEXPERIMENTs - -Adds crypto backend GOEXPERIMENTs and modifies tag handling to make -goexperiment.systemcrypto behave as an alias that enables the recommended -backend for the target GOOS. See src/internal/goexperiment/flags.go for more -information about the behavior. - -Includes new tests in "build_test.go" and "buildbackend_test.go" to help -maintain this feature. For more information, see the test files. ---- - src/cmd/go/alldocs.go | 3 + - src/cmd/go/internal/envcmd/env.go | 3 +- - src/cmd/go/internal/help/helpdoc.go | 3 + - src/cmd/go/internal/modindex/build_test.go | 73 +++++++++++++++++++ - src/cmd/go/testdata/script/env_changed.txt | 3 + - src/go/build/buildbackend_test.go | 50 +++++++++++++ - .../build/testdata/backendtags_system/main.go | 3 + - .../backendtags_system/systemcrypto.go | 3 + - src/internal/buildcfg/cfg.go | 2 + - src/internal/buildcfg/exp.go | 34 +++++++++ - src/internal/cfg/cfg.go | 1 + - .../goexperiment/exp_cngcrypto_off.go | 8 ++ - src/internal/goexperiment/exp_cngcrypto_on.go | 8 ++ - .../goexperiment/exp_darwincrypto_off.go | 8 ++ - .../goexperiment/exp_darwincrypto_on.go | 8 ++ - .../goexperiment/exp_opensslcrypto_off.go | 8 ++ - .../goexperiment/exp_opensslcrypto_on.go | 8 ++ - .../goexperiment/exp_systemcrypto_off.go | 8 ++ - .../goexperiment/exp_systemcrypto_on.go | 8 ++ - src/internal/goexperiment/flags.go | 18 +++++ - src/internal/platform/supported.go | 12 +++ - 21 files changed, 271 insertions(+), 1 deletion(-) - create mode 100644 src/cmd/go/internal/modindex/build_test.go - create mode 100644 src/go/build/buildbackend_test.go - create mode 100644 src/go/build/testdata/backendtags_system/main.go - create mode 100644 src/go/build/testdata/backendtags_system/systemcrypto.go - create mode 100644 src/internal/goexperiment/exp_cngcrypto_off.go - create mode 100644 src/internal/goexperiment/exp_cngcrypto_on.go - create mode 100644 src/internal/goexperiment/exp_darwincrypto_off.go - create mode 100644 src/internal/goexperiment/exp_darwincrypto_on.go - create mode 100644 src/internal/goexperiment/exp_opensslcrypto_off.go - create mode 100644 src/internal/goexperiment/exp_opensslcrypto_on.go - create mode 100644 src/internal/goexperiment/exp_systemcrypto_off.go - create mode 100644 src/internal/goexperiment/exp_systemcrypto_on.go - -diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go -index 74cafe9613917d..31ce076273aba2 100644 ---- a/src/cmd/go/alldocs.go -+++ b/src/cmd/go/alldocs.go -@@ -2616,6 +2616,9 @@ - // mentioned will be considered insecure by 'go get'. - // Because the variable is defined by Git, the default value cannot - // be set using 'go env -w'. -+// MS_GO_NOSYSTEMCRYPTO -+// Whether to disable the use of the system's cryptography libraries. -+// Set to 1 to disable the use of system cryptography libraries. - // - // Additional information available from 'go env' but not read from the environment: - // -diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go -index ed23b856f132c6..58ce4adbc4e394 100644 ---- a/src/cmd/go/internal/envcmd/env.go -+++ b/src/cmd/go/internal/envcmd/env.go -@@ -125,6 +125,7 @@ func MkEnv() []cfg.EnvVar { - {Name: "GOTOOLDIR", Value: build.ToolDir}, - {Name: "GOVCS", Value: cfg.GOVCS}, - {Name: "GOVERSION", Value: runtime.Version()}, -+ {Name: "MS_GO_NOSYSTEMCRYPTO", Value: os.Getenv("MS_GO_NOSYSTEMCRYPTO")}, - } - - for i := range env { -@@ -133,7 +134,7 @@ func MkEnv() []cfg.EnvVar { - if env[i].Value != "on" && env[i].Value != "" { - env[i].Changed = true - } -- case "GOBIN", "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPACKAGESDRIVER", "GOPRIVATE", "GOTMPDIR", "GOVCS": -+ case "GOBIN", "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPACKAGESDRIVER", "GOPRIVATE", "GOTMPDIR", "GOVCS", "MS_GO_NOSYSTEMCRYPTO": - if env[i].Value != "" { - env[i].Changed = true - } -diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go -index 4f76f7f9c1fc68..783d11609c0bf4 100644 ---- a/src/cmd/go/internal/help/helpdoc.go -+++ b/src/cmd/go/internal/help/helpdoc.go -@@ -755,6 +755,9 @@ Special-purpose environment variables: - mentioned will be considered insecure by 'go get'. - Because the variable is defined by Git, the default value cannot - be set using 'go env -w'. -+ MS_GO_NOSYSTEMCRYPTO -+ Whether to disable the use of the system's cryptography libraries. -+ Set to 1 to disable the use of system cryptography libraries. - - Additional information available from 'go env' but not read from the environment: - -diff --git a/src/cmd/go/internal/modindex/build_test.go b/src/cmd/go/internal/modindex/build_test.go -new file mode 100644 -index 00000000000000..1756c5d027fee0 ---- /dev/null -+++ b/src/cmd/go/internal/modindex/build_test.go -@@ -0,0 +1,73 @@ -+// Copyright 2023 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+// This file is not a copy. It tests that the copied code in this directory is -+// maintained. Specifically, it tests areas that are modified by microsoft/go. -+// The files also contain intentional modifications, so it isn't reasonable (as -+// of writing) to test that the entire file is identical. -+ -+package modindex -+ -+import ( -+ "flag" -+ "os" -+ "strings" -+ "testing" -+) -+ -+var fixCopy = flag.Bool("fixcopy", false, "if true, update some copied code in build.go") -+ -+func TestCopyIdentical(t *testing.T) { -+ originalBytes, err := os.ReadFile("../../../../go/build/build.go") -+ if err != nil { -+ t.Fatal(err) -+ } -+ wantCode := string(originalBytes) -+ -+ gotBytes, err := os.ReadFile("build.go") -+ if err != nil { -+ t.Fatal(err) -+ } -+ gotCode := string(gotBytes) -+ -+ tests := []struct { -+ name string -+ prefix string -+ suffix string -+ }{ -+ {"matchTag", "func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {", "\n}"}, -+ } -+ for _, tt := range tests { -+ t.Run(tt.name, func(t *testing.T) { -+ var want, got string -+ if _, after, ok := strings.Cut(wantCode, tt.prefix); ok { -+ if before, _, ok := strings.Cut(after, tt.suffix); ok { -+ want = before -+ } else { -+ t.Fatal("suffix not found in original file") -+ } -+ } else { -+ t.Fatal("prefix not found in original file") -+ } -+ if _, after, ok := strings.Cut(gotCode, tt.prefix); ok { -+ if before, _, ok := strings.Cut(after, tt.suffix); ok { -+ got = before -+ } else { -+ t.Fatal("suffix not found in copied file") -+ } -+ } else { -+ t.Fatal("prefix not found in copied file") -+ } -+ if got != want { -+ if *fixCopy { -+ if err := os.WriteFile("build.go", []byte(strings.Replace(gotCode, got, want, 1)), 0o666); err != nil { -+ t.Fatal(err) -+ } -+ } else { -+ t.Error("copy is not the same as original; use '-fixcopy' to replace copied code with the code from the original file") -+ } -+ } -+ }) -+ } -+} -diff --git a/src/cmd/go/testdata/script/env_changed.txt b/src/cmd/go/testdata/script/env_changed.txt -index da9560062704e6..a12836ff65fd03 100644 ---- a/src/cmd/go/testdata/script/env_changed.txt -+++ b/src/cmd/go/testdata/script/env_changed.txt -@@ -13,6 +13,7 @@ env CGO_CPPFLAGS=nodefault - env GOFIPS140=latest - [cgo] env CGO_ENABLED=0 - env GCCGO=nodefault -+env MS_GO_NOSYSTEMCRYPTO=1 - - go env -changed - # linux output like GOTOOLCHAIN='local' -@@ -26,6 +27,7 @@ stdout 'CGO_CPPFLAGS=''?nodefault''?' - stdout 'GOFIPS140=''?latest''?' - [cgo] stdout 'CGO_ENABLED=''?0''?' - stdout 'GCCGO=''?nodefault''?' -+stdout 'MS_GO_NOSYSTEMCRYPTO=''?1''?' - - go env -changed -json - stdout '"GOTOOLCHAIN": "local"' -@@ -37,6 +39,7 @@ stdout '"CGO_CPPFLAGS": "nodefault"' - stdout '"GOFIPS140": "latest"' - [cgo] stdout '"CGO_ENABLED": "0"' - stdout '"GCCGO": "nodefault"' -+stdout '"MS_GO_NOSYSTEMCRYPTO": "1"' - - [GOOS:windows] env GOOS=linux - [!GOOS:windows] env GOOS=windows -diff --git a/src/go/build/buildbackend_test.go b/src/go/build/buildbackend_test.go -new file mode 100644 -index 00000000000000..ffb835ce34a2f7 ---- /dev/null -+++ b/src/go/build/buildbackend_test.go -@@ -0,0 +1,50 @@ -+// Copyright 2023 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+package build -+ -+import ( -+ "reflect" -+ "testing" -+) -+ -+// Check that the systemcrypto tag works and collects AllTags correctly. -+// This is based on the TestAllTags test. -+func TestCryptoBackendAllTags(t *testing.T) { -+ ctxt := Default -+ // Remove tool tags so these tests behave the same regardless of the -+ // goexperiments that happen to be set during the run. -+ ctxt.ToolTags = []string{} -+ ctxt.GOARCH = "amd64" -+ ctxt.GOOS = "linux" -+ ctxt.BuildTags = []string{"goexperiment.systemcrypto"} -+ -+ p, err := ctxt.ImportDir("testdata/backendtags_system", 0) -+ if err != nil { -+ t.Fatal(err) -+ } -+ want := []string{"goexperiment.systemcrypto"} -+ if !reflect.DeepEqual(p.AllTags, want) { -+ t.Errorf("AllTags = %v, want %v", p.AllTags, want) -+ } -+ wantFiles := []string{"main.go", "systemcrypto.go"} -+ if !reflect.DeepEqual(p.GoFiles, wantFiles) { -+ t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) -+ } -+ -+ // Test without systemcrypto - only main.go should be included -+ ctxt.BuildTags = []string{} -+ p, err = ctxt.ImportDir("testdata/backendtags_system", 0) -+ if err != nil { -+ t.Fatal(err) -+ } -+ want = []string{"goexperiment.systemcrypto"} -+ if !reflect.DeepEqual(p.AllTags, want) { -+ t.Errorf("AllTags = %v, want %v", p.AllTags, want) -+ } -+ wantFiles = []string{"main.go"} -+ if !reflect.DeepEqual(p.GoFiles, wantFiles) { -+ t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) -+ } -+} -diff --git a/src/go/build/testdata/backendtags_system/main.go b/src/go/build/testdata/backendtags_system/main.go -new file mode 100644 -index 00000000000000..38dd16da61accb ---- /dev/null -+++ b/src/go/build/testdata/backendtags_system/main.go -@@ -0,0 +1,3 @@ -+package main -+ -+func main() {} -diff --git a/src/go/build/testdata/backendtags_system/systemcrypto.go b/src/go/build/testdata/backendtags_system/systemcrypto.go -new file mode 100644 -index 00000000000000..eb8a026982259c ---- /dev/null -+++ b/src/go/build/testdata/backendtags_system/systemcrypto.go -@@ -0,0 +1,3 @@ -+//go:build goexperiment.systemcrypto -+ -+package main -diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go -index 89fd74eb823162..6d266ff641d721 100644 ---- a/src/internal/buildcfg/cfg.go -+++ b/src/internal/buildcfg/cfg.go -@@ -38,6 +38,8 @@ var ( - Version = version - ) - -+var SystemCryptoDisabled = os.Getenv("MS_GO_NOSYSTEMCRYPTO") == "1" -+ - // Error is one of the errors found (if any) in the build configuration. - var Error error - -diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go -index 8eeef17c1f470e..d474ed1d4d466d 100644 ---- a/src/internal/buildcfg/exp.go -+++ b/src/internal/buildcfg/exp.go -@@ -6,6 +6,7 @@ package buildcfg - - import ( - "fmt" -+ "os" - "reflect" - "strings" - -@@ -29,6 +30,19 @@ type ExperimentFlags struct { - // default in the current toolchain. This is, in effect, the "control" - // configuration and any variation from this is an experiment. - var Experiment ExperimentFlags = func() ExperimentFlags { -+ if SystemCryptoDisabled { -+ // If MS_GO_NOSYSTEMCRYPTO is set, ensure that nosystemcrypto is in GOEXPERIMENT. -+ // We do it here because it is the earliest point where the toolchain tries to retrive -+ // the GOEXPERIMENT value. -+ // The environment variable is updated rather than appending "nosystemcrypto" to the -+ // result of ParseGOEXPERIMENT because the latter will be moved at some point to -+ // internal/goexperiment, which can't import the os package. -+ v := os.Getenv("GOEXPERIMENT") -+ if v != "" { -+ v += "," -+ } -+ os.Setenv("GOEXPERIMENT", v+"nosystemcrypto") -+ } - flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT)) - if err != nil { - Error = err -@@ -67,6 +81,18 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { - regabiSupported = true - } - -+ // These are the goos/goarch combinations where systemcrypto is -+ // enabled by default. -+ var systemCryptoSupported bool -+ switch goos { -+ case "windows": -+ systemCryptoSupported = goarch == "amd64" || goarch == "arm64" -+ case "linux": -+ systemCryptoSupported = true -+ case "darwin": -+ systemCryptoSupported = true -+ } -+ - // Older versions (anything before V16) of dsymutil don't handle - // the .debug_rnglists section in DWARF5. See - // https://github.com/golang/go/issues/26379#issuecomment-2677068742 -@@ -79,6 +105,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { - dwarf5Supported := (goos != "darwin" && goos != "ios" && goos != "aix") - - baseline := goexperiment.Flags{ -+ SystemCrypto: systemCryptoSupported, - RegabiWrappers: regabiSupported, - RegabiArgs: regabiSupported, - Dwarf5: dwarf5Supported, -@@ -126,6 +153,10 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { - // to build with any experiment flags. - flags.Flags = goexperiment.Flags{} - continue -+ } else if f == "nosystemcrypto" { -+ // GOEXPERIMENT=nosystemcrypto disables system crypto. -+ flags.Flags.SystemCrypto = false -+ continue - } - val := true - if strings.HasPrefix(f, "no") { -@@ -152,6 +183,9 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { - if flags.RegabiArgs && !flags.RegabiWrappers { - return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers") - } -+ if flags.BoringCrypto { -+ return nil, fmt.Errorf("GOEXPERIMENT boringcrypto is not supported in the Microsoft build of Go") -+ } - return flags, nil - } - -diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go -index 9329769721b7de..1724afb71b851e 100644 ---- a/src/internal/cfg/cfg.go -+++ b/src/internal/cfg/cfg.go -@@ -70,5 +70,6 @@ const KnownEnv = ` - GOWASM - GOWORK - GO_EXTLINK_ENABLED -+ MS_GO_NOSYSTEMCRYPTO - PKG_CONFIG - ` -diff --git a/src/internal/goexperiment/exp_cngcrypto_off.go b/src/internal/goexperiment/exp_cngcrypto_off.go -new file mode 100644 -index 00000000000000..eb879f94fa0c42 ---- /dev/null -+++ b/src/internal/goexperiment/exp_cngcrypto_off.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build !goexperiment.cngcrypto -+ -+package goexperiment -+ -+const CNGCrypto = false -+const CNGCryptoInt = 0 -diff --git a/src/internal/goexperiment/exp_cngcrypto_on.go b/src/internal/goexperiment/exp_cngcrypto_on.go -new file mode 100644 -index 00000000000000..5b0a55d6c5772e ---- /dev/null -+++ b/src/internal/goexperiment/exp_cngcrypto_on.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build goexperiment.cngcrypto -+ -+package goexperiment -+ -+const CNGCrypto = true -+const CNGCryptoInt = 1 -diff --git a/src/internal/goexperiment/exp_darwincrypto_off.go b/src/internal/goexperiment/exp_darwincrypto_off.go -new file mode 100644 -index 00000000000000..331111ce4759f7 ---- /dev/null -+++ b/src/internal/goexperiment/exp_darwincrypto_off.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build !goexperiment.darwincrypto -+ -+package goexperiment -+ -+const DarwinCrypto = false -+const DarwinCryptoInt = 0 -diff --git a/src/internal/goexperiment/exp_darwincrypto_on.go b/src/internal/goexperiment/exp_darwincrypto_on.go -new file mode 100644 -index 00000000000000..4bf785b999ecce ---- /dev/null -+++ b/src/internal/goexperiment/exp_darwincrypto_on.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build goexperiment.darwincrypto -+ -+package goexperiment -+ -+const DarwinCrypto = true -+const DarwinCryptoInt = 1 -diff --git a/src/internal/goexperiment/exp_opensslcrypto_off.go b/src/internal/goexperiment/exp_opensslcrypto_off.go -new file mode 100644 -index 00000000000000..b28c0976a94cb0 ---- /dev/null -+++ b/src/internal/goexperiment/exp_opensslcrypto_off.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build !goexperiment.opensslcrypto -+ -+package goexperiment -+ -+const OpenSSLCrypto = false -+const OpenSSLCryptoInt = 0 -diff --git a/src/internal/goexperiment/exp_opensslcrypto_on.go b/src/internal/goexperiment/exp_opensslcrypto_on.go -new file mode 100644 -index 00000000000000..154386b3de81ea ---- /dev/null -+++ b/src/internal/goexperiment/exp_opensslcrypto_on.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build goexperiment.opensslcrypto -+ -+package goexperiment -+ -+const OpenSSLCrypto = true -+const OpenSSLCryptoInt = 1 -diff --git a/src/internal/goexperiment/exp_systemcrypto_off.go b/src/internal/goexperiment/exp_systemcrypto_off.go -new file mode 100644 -index 00000000000000..6afc2ef2c5822e ---- /dev/null -+++ b/src/internal/goexperiment/exp_systemcrypto_off.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build !goexperiment.systemcrypto -+ -+package goexperiment -+ -+const SystemCrypto = false -+const SystemCryptoInt = 0 -diff --git a/src/internal/goexperiment/exp_systemcrypto_on.go b/src/internal/goexperiment/exp_systemcrypto_on.go -new file mode 100644 -index 00000000000000..fcd4cb9da0d162 ---- /dev/null -+++ b/src/internal/goexperiment/exp_systemcrypto_on.go -@@ -0,0 +1,8 @@ -+// Code generated by mkconsts.go. DO NOT EDIT. -+ -+//go:build goexperiment.systemcrypto -+ -+package goexperiment -+ -+const SystemCrypto = true -+const SystemCryptoInt = 1 -diff --git a/src/internal/goexperiment/flags.go b/src/internal/goexperiment/flags.go -index 17fe15270aba60..5f09ad11a701e2 100644 ---- a/src/internal/goexperiment/flags.go -+++ b/src/internal/goexperiment/flags.go -@@ -60,6 +60,24 @@ type Flags struct { - StaticLockRanking bool - BoringCrypto bool - -+ // SystemCrypto enables the system crypto backend for the target GOOS. -+ // The specific backend used depends on the platform: -+ // - Linux: OpenSSL -+ // - Windows: CNG -+ // - Darwin: CommonCrypto/Security.framework -+ // -+ // Platform-specific code should use build constraints like: -+ // //go:build goexperiment.systemcrypto && linux -+ // //go:build goexperiment.systemcrypto && windows -+ // //go:build goexperiment.systemcrypto && darwin -+ SystemCrypto bool -+ -+ // OpenSSLCrypto, CNGCrypto, and DarwinCrypto are deprecated. -+ // Use SystemCrypto instead. -+ OpenSSLCrypto bool -+ CNGCrypto bool -+ DarwinCrypto bool -+ - // Regabi is split into several sub-experiments that can be - // enabled individually. Not all combinations work. - // The "regabi" GOEXPERIMENT is an alias for all "working" -diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go -index 6f37e368596498..471c5cc430bb29 100644 ---- a/src/internal/platform/supported.go -+++ b/src/internal/platform/supported.go -@@ -279,3 +279,15 @@ func FirstClass(goos, goarch string) bool { - func Broken(goos, goarch string) bool { - return distInfo[OSArch{goos, goarch}].Broken - } -+ -+// SystemCryptoSupported reports whether goos/goarch supports system crypto. -+func SystemCryptoSupported(goos, goarch string) bool { -+ switch goos { -+ case "linux", "darwin": -+ return true -+ case "windows": -+ return goarch == "amd64" || goarch == "arm64" -+ default: -+ return false -+ } -+} diff --git a/patches/0002-Add-crypto-backends.patch b/patches/0002-Add-crypto-backends.patch new file mode 100644 index 00000000000..89ff4455509 --- /dev/null +++ b/patches/0002-Add-crypto-backends.patch @@ -0,0 +1,8552 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: bot-for-go[bot] <199222863+bot-for-go[bot]@users.noreply.github.com> +Date: Wed, 31 May 2023 16:54:31 -0500 +Subject: [PATCH] Add crypto backends + +--- + .gitignore | 2 + + src/cmd/api/boring_test.go | 2 +- + .../compile/internal/logopt/logopt_test.go | 5 + + src/cmd/compile/script_test.go | 8 + + src/cmd/compile/testdata/script/README | 2 + + src/cmd/dist/build.go | 68 ++- + src/cmd/dist/test.go | 48 +- + src/cmd/go/alldocs.go | 3 + + src/cmd/go/go_boring_test.go | 6 +- + src/cmd/go/go_test.go | 11 + + src/cmd/go/internal/cfg/cfg.go | 32 +- + src/cmd/go/internal/envcmd/env.go | 3 +- + src/cmd/go/internal/help/helpdoc.go | 3 + + src/cmd/go/internal/load/pkg.go | 4 + + src/cmd/go/internal/modindex/build_test.go | 73 +++ + src/cmd/go/internal/tool/tool.go | 9 +- + .../verylongtest/testdata/script/README | 2 + + src/cmd/go/script_test.go | 3 + + src/cmd/go/systemcrypto_test.go | 272 +++++++++++ + src/cmd/go/testdata/script/README | 2 + + src/cmd/go/testdata/script/darwin_no_cgo.txt | 1 + + src/cmd/go/testdata/script/env_changed.txt | 3 + + .../go/testdata/script/env_cross_build.txt | 2 + + .../go/testdata/script/gopath_std_vendor.txt | 9 + + .../script/test_android_issue62123.txt | 2 + + .../internal/fuzztest/testdata/script/README | 2 + + .../internal/script/scripttest/conditions.go | 10 +- + src/cmd/internal/testdir/testdir_test.go | 6 + + src/cmd/link/internal/ld/config.go | 7 + + src/cmd/link/internal/ld/lib.go | 6 + + src/cmd/link/link_test.go | 8 + + src/cmd/link/testdata/script/README | 2 + + src/crypto/aes/aes.go | 2 +- + src/crypto/aes/aes_test.go | 2 +- + src/crypto/boring/boring.go | 4 +- + src/crypto/cipher/cbc.go | 16 + + src/crypto/cipher/ctr.go | 7 + + src/crypto/cipher/ctr_aes_test.go | 2 +- + src/crypto/cipher/gcm.go | 59 ++- + src/crypto/cipher/gcm_test.go | 9 +- + src/crypto/des/cipher.go | 7 + + src/crypto/dsa/boring.go | 113 +++++ + src/crypto/dsa/dsa.go | 88 ++++ + src/crypto/dsa/dsa_test.go | 8 + + src/crypto/dsa/notboring.go | 16 + + src/crypto/ecdh/ecdh.go | 2 +- + src/crypto/ecdh/ecdh_test.go | 17 +- + src/crypto/ecdh/nist.go | 30 +- + src/crypto/ecdh/x25519.go | 49 ++ + src/crypto/ecdsa/badlinkname.go | 19 + + src/crypto/ecdsa/boring.go | 6 +- + src/crypto/ecdsa/ecdsa.go | 10 +- + src/crypto/ecdsa/notboring.go | 4 +- + src/crypto/ed25519/boring.go | 71 +++ + src/crypto/ed25519/ed25519.go | 70 +++ + src/crypto/ed25519/ed25519_test.go | 14 +- + src/crypto/ed25519/notboring.go | 16 + + src/crypto/fips140/enforcement_test.go | 4 + + src/crypto/fips140/fips140.go | 3 +- + src/crypto/hkdf/hkdf.go | 14 + + src/crypto/hkdf/hkdf_test.go | 2 +- + src/crypto/hmac/hmac.go | 16 +- + src/crypto/hmac/hmac_test.go | 2 +- + src/crypto/hpke/aead.go | 14 +- + src/crypto/internal/backend/backend_darwin.go | 441 ++++++++++++++++++ +src/crypto/internal/backend/backend_linux.go | 434 +++++++++++++++++ + src/crypto/internal/backend/backend_test.go | 56 +++ + .../internal/backend/backend_windows.go | 441 ++++++++++++++++++ + src/crypto/internal/backend/bbig/big.go | 17 + + .../internal/backend/bbig/big_darwin.go | 12 + + src/crypto/internal/backend/bbig/big_linux.go | 12 + + .../internal/backend/bbig/big_windows.go | 12 + + src/crypto/internal/backend/common.go | 51 ++ + .../internal/backend/fips140/fips140.go | 15 + + .../internal/fips140state/nosystemcrypto.go | 11 + + .../internal/fips140state/isrequirefips.go | 9 + + .../internal/fips140state/norequirefips.go | 9 + + .../backend/internal/fips140state/state.go | 91 ++++ + .../backend/internal/fips140state/state_test.go | 75 +++ + .../internal/fips140state/skipfipscheck_off.go | 9 + + .../internal/fips140state/skipfipscheck_on.go | 9 + + .../fips140state/requirefips_nosystemcrypto.go | 15 + + .../internal/fips140state/systemfips_darwin.go | 11 + + .../internal/fips140state/systemfips_linux.go | 57 +++ + .../fips140state/systemfips_windows.go | 33 ++ + .../opensslsetup/opensslsetup_linux.go | 68 +++ + .../opensslsetup/opensslsetup_linux_test.go | 92 ++++ + .../backend/internal/opensslsetup/stub.go | 8 + + src/crypto/internal/backend/nobackend.go | 375 +++++++++++++++ + src/crypto/internal/backend/stub.s | 10 + + src/crypto/internal/cryptotest/allocations.go | 6 - + src/crypto/internal/cryptotest/fips140.go | 8 + + src/crypto/internal/cryptotest/hash.go | 3 +- + .../internal/cryptotest/implementations.go | 2 +- + src/crypto/internal/fips140hash/hash.go | 3 +- + .../internal/fips140only/fips140only.go | 11 +- + .../internal/fips140only/fips140only_test.go | 45 +- + src/crypto/internal/fips140test/acvp_test.go | 6 + + src/crypto/internal/fips140test/cast_test.go | 2 + + src/crypto/internal/fips140test/fips_test.go | 2 +- + src/crypto/internal/rand/rand.go | 2 +- + src/crypto/md5/md5.go | 10 + + src/crypto/md5/md5_test.go | 18 +- + src/crypto/mlkem/mlkem.go | 118 ++++- + src/crypto/mlkem/mlkem_test.go | 8 + + src/crypto/pbkdf2/pbkdf2.go | 7 + + src/crypto/pbkdf2/pbkdf2_test.go | 6 +- + src/crypto/purego_test.go | 2 +- + src/crypto/rand/rand.go | 2 +- + src/crypto/rand/rand_test.go | 9 +- + src/crypto/rc4/rc4.go | 18 + + src/crypto/rsa/boring.go | 12 +- + src/crypto/rsa/boring_test.go | 2 +- + src/crypto/rsa/fips.go | 94 ++-- + src/crypto/rsa/notboring.go | 4 +- + src/crypto/rsa/pkcs1v15.go | 10 +- + src/crypto/rsa/pkcs1v15_test.go | 5 + + src/crypto/rsa/pss_test.go | 14 +- + src/crypto/rsa/rsa.go | 29 +- + src/crypto/rsa/rsa_darwin.go | 71 +++ + src/crypto/rsa/rsa_test.go | 7 +- + src/crypto/sha1/sha1.go | 11 +- + src/crypto/sha1/sha1_test.go | 11 +- + src/crypto/sha256/sha256.go | 6 +- + src/crypto/sha256/sha256_test.go | 44 +- + src/crypto/sha3/sha3.go | 129 ++++- + src/crypto/sha3/sha3_test.go | 18 +- + src/crypto/sha512/sha512.go | 14 +- + src/crypto/sha512/sha512_test.go | 32 +- + src/crypto/systemcrypto_nocgo_linux.go | 18 + + src/crypto/tls/cipher_suites.go | 10 +- + src/crypto/tls/fipsonly/fipsonly.go | 2 +- + src/crypto/tls/fipsonly/fipsonly_test.go | 2 +- + src/crypto/tls/handshake_client.go | 17 +- + src/crypto/tls/handshake_client_tls13.go | 2 +- + src/crypto/tls/handshake_server.go | 15 +- + src/crypto/tls/handshake_server_tls13.go | 5 +- + src/crypto/tls/internal/tls13/doc.go | 18 + + src/crypto/tls/internal/tls13/tls13.go | 195 ++++++++ + src/crypto/tls/key_schedule.go | 2 +- + src/crypto/tls/prf.go | 24 + + src/crypto/tls/prf_test.go | 9 + + src/crypto/x509/verify_test.go | 2 +- + src/go/build/buildbackend_test.go | 50 ++ + src/go/build/deps_test.go | 39 +- + .../build/testdata/backendtags_system/main.go | 3 + + .../backendtags_system/systemcrypto.go | 3 + + src/hash/boring_test.go | 9 + + src/hash/example_test.go | 2 + + src/hash/marshal_test.go | 4 + + src/hash/notboring_test.go | 9 + + src/internal/buildcfg/cfg.go | 2 + + src/internal/buildcfg/cfg_test.go | 44 ++ + src/internal/buildcfg/exp.go | 20 + + src/internal/cfg/cfg.go | 1 + + src/internal/platform/supported.go | 12 + + src/internal/systemcrypto/systemcrypto.go | 20 + + .../systemcrypto/systemcrypto_test.go | 58 +++ + src/net/lookup_test.go | 3 + + src/os/exec/exec_test.go | 9 + + src/runtime/runtime_boring.go | 5 + + 161 files changed, 4834 insertions(+), 269 deletions(-) + create mode 100644 src/cmd/go/internal/modindex/build_test.go + create mode 100644 src/cmd/go/systemcrypto_test.go + create mode 100644 src/crypto/dsa/boring.go + create mode 100644 src/crypto/dsa/notboring.go + create mode 100644 src/crypto/ecdsa/badlinkname.go + create mode 100644 src/crypto/ed25519/boring.go + create mode 100644 src/crypto/ed25519/notboring.go + create mode 100644 src/crypto/internal/backend/backend_darwin.go + create mode 100644 src/crypto/internal/backend/backend_linux.go + create mode 100644 src/crypto/internal/backend/backend_test.go + create mode 100644 src/crypto/internal/backend/backend_windows.go + create mode 100644 src/crypto/internal/backend/bbig/big.go + create mode 100644 src/crypto/internal/backend/bbig/big_darwin.go + create mode 100644 src/crypto/internal/backend/bbig/big_linux.go + create mode 100644 src/crypto/internal/backend/bbig/big_windows.go + create mode 100644 src/crypto/internal/backend/common.go + create mode 100644 src/crypto/internal/backend/fips140/fips140.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/isrequirefips.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/nosystemcrypto.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/norequirefips.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/requirefips_nosystemcrypto.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/state.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/state_test.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/skipfipscheck_off.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/skipfipscheck_on.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/systemfips_darwin.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/systemfips_linux.go + create mode 100644 src/crypto/internal/backend/internal/fips140state/systemfips_windows.go + create mode 100644 src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go + create mode 100644 src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go + create mode 100644 src/crypto/internal/backend/internal/opensslsetup/stub.go + create mode 100644 src/crypto/internal/backend/nobackend.go + create mode 100644 src/crypto/internal/backend/stub.s + create mode 100644 src/crypto/rsa/rsa_darwin.go + create mode 100644 src/crypto/systemcrypto_nocgo_linux.go + create mode 100644 src/crypto/tls/internal/tls13/doc.go + create mode 100644 src/crypto/tls/internal/tls13/tls13.go + create mode 100644 src/go/build/buildbackend_test.go + create mode 100644 src/go/build/testdata/backendtags_system/main.go + create mode 100644 src/go/build/testdata/backendtags_system/systemcrypto.go + create mode 100644 src/hash/boring_test.go + create mode 100644 src/hash/notboring_test.go + create mode 100644 src/internal/systemcrypto/systemcrypto.go + create mode 100644 src/internal/systemcrypto/systemcrypto_test.go + +diff --git a/.gitignore b/.gitignore +index 344b31f7ac1c1d..b4583113d5aa15 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -47,6 +47,8 @@ _testmain.go + /test/run.out + /test/times.out + ++!/src/vendor/github.com/microsoft/go-crypto-darwin/internal/cryptokit/CryptoKit.o ++ + # This file includes artifacts of Go build that should not be checked in. + # For files created by specific development environment (e.g. editor), + # use alternative ways to exclude files from git. +diff --git a/src/cmd/api/boring_test.go b/src/cmd/api/boring_test.go +index f0e3575637c62a..9eab3b4e66e60b 100644 +--- a/src/cmd/api/boring_test.go ++++ b/src/cmd/api/boring_test.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + package main + +diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go +index 1edabf9fb7ff04..5e9fc3507485bc 100644 +--- a/src/cmd/compile/internal/logopt/logopt_test.go ++++ b/src/cmd/compile/internal/logopt/logopt_test.go +@@ -5,6 +5,7 @@ + package logopt + + import ( ++ "internal/systemcrypto" + "internal/testenv" + "os" + "path/filepath" +@@ -148,6 +149,10 @@ func s15a8(x *[15]int64) [15]int64 { + goos0 := runtime.GOOS + if runtime.GOARCH == "amd64" { // Test many things with "linux" (wasm will get "js") + arches = []string{"arm", "arm64", "386", "amd64", "mips", "mips64", "loong64", "ppc64le", "riscv64", "s390x", "wasm"} ++ if systemcrypto.Enabled() { ++ // SystemCrypto does not support cross-compilation on all architectures. ++ arches = []string{"arm", "arm64", "386", "amd64", "loong64", "ppc64le", "riscv64", "s390x"} ++ } + goos0 = "linux" + } + +diff --git a/src/cmd/compile/script_test.go b/src/cmd/compile/script_test.go +index 0e32e0769ee2e2..552dfe13dd6a05 100644 +--- a/src/cmd/compile/script_test.go ++++ b/src/cmd/compile/script_test.go +@@ -7,6 +7,7 @@ package main + import ( + "cmd/internal/script/scripttest" + "flag" ++ "internal/systemcrypto" + "internal/testenv" + "os" + "runtime" +@@ -50,6 +51,13 @@ func TestScript(t *testing.T) { + // the installed linker with our test binary. + doReplacement = false + } ++ if systemcrypto.Enabled() { ++ // If systemcrypto is enabled, we can't replace the compiler ++ // because the test binary is built with systemcrypto, but the ++ // compiler is not. This would cause a mismatch in the ++ // compiler version check in the script tests. ++ doReplacement = false ++ } + repls := []scripttest.ToolReplacement{} + if doReplacement { + if testCompiler == "" { +diff --git a/src/cmd/compile/testdata/script/README b/src/cmd/compile/testdata/script/README +index 9ec997a138f0f2..09c4e7955ec162 100644 +--- a/src/cmd/compile/testdata/script/README ++++ b/src/cmd/compile/testdata/script/README +@@ -281,6 +281,8 @@ The available conditions are: + testing.Short() + [symlink] + testenv.HasSymlink() ++[systemcrypto] ++ system crypto is enabled + [verbose] + testing.Verbose() + +diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go +index 78d55bec559987..decf356a449ba9 100644 +--- a/src/cmd/dist/build.go ++++ b/src/cmd/dist/build.go +@@ -1399,6 +1399,68 @@ func toolenv() []string { + // still have full paths for stack traces for compiler crashes and the like. + env = append(env, "GOFLAGS=-trimpath -ldflags=-w -gcflags=cmd/...=-dwarf=false") + } ++ return toolenvMicrosoft(env) ++} ++ ++// updateEnv updates the environment variable named name in env ++// by applying the function fn to its value. ++// If the variable is not present in env, the value is taken from ++// the current environment (os.Getenv), and the updated value is appended ++// to env. ++// If the variable is not present in the current environment, the new value ++// is appended to env. ++func updateEnv(name string, env []string, fn func(string) string) []string { ++ // Find the variable in env. ++ for i, e := range env { ++ if value, ok := strings.CutPrefix(e, name+"="); ok { ++ // Found it, update it. ++ env[i] = name + "=" + fn(value) ++ return env ++ } ++ } ++ ++ // Not found, use the current environment. ++ val := os.Getenv(name) ++ if val == "" { ++ val = fn("") ++ } else { ++ val = fn(val) ++ } ++ return append(env, name+"="+val) ++} ++ ++// toolenvMicrosoft appends Microsoft-specific environment variables to env. ++func toolenvMicrosoft(env []string) []string { ++ // Disable system crypto while building the Microsoft build of Go to favor ++ // portability. Go only uses crypto packages for non-crypto purposes, such as ++ // generating build IDs. ++ env = updateEnv("MS_GO_NOSYSTEMCRYPTO", env, func(value string) string { ++ return "1" ++ }) ++ ++ // Build the Go toolchain with "-tags=ms_skipfipscheck". This ++ // allows toolchains to be used when GODEBUG=fips140=on is set. For example, when running ++ // "GODEBUG=fips140=on go test ./..." or "GODEBUG=fips140=on go run .". ++ env = updateEnv("GOFLAGS", env, func(value string) string { ++ if value == "" { ++ return "-tags=ms_skipfipscheck" ++ } ++ goflags := strings.Fields(value) ++ // If -tags= is already set, append "ms_skipfipscheck" to it. ++ // If not, add it as a new flag. ++ var found bool ++ for i, e := range goflags { ++ if value, ok := strings.CutPrefix(e, "-tags="); ok { ++ goflags[i] = "-tags=" + value + ",ms_skipfipscheck" ++ found = true ++ break ++ } ++ } ++ if !found { ++ goflags = append(goflags, "-tags=ms_skipfipscheck") ++ } ++ return strings.Join(goflags, " ") ++ }) + return env + } + +@@ -1641,12 +1703,12 @@ func cmdbootstrap() { + os.Setenv("CC", compilerEnvLookup("CC", defaultcc, goos, goarch)) + xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch) + } +- goInstall(nil, goBootstrap, "std") ++ goInstall(toolenvMicrosoft(nil), goBootstrap, "std") + goInstall(toolenv(), goBootstrap, toolsToInstall...) + checkNotStale(toolenv(), goBootstrap, toolchain...) +- checkNotStale(nil, goBootstrap, "std") ++ checkNotStale(toolenvMicrosoft(nil), goBootstrap, "std") + checkNotStale(toolenv(), goBootstrap, toolsToInstall...) +- checkNotStale(nil, gorootBinGo, "std") ++ checkNotStale(toolenvMicrosoft(nil), gorootBinGo, "std") + checkNotStale(toolenv(), gorootBinGo, toolsToInstall...) + if debug { + run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") +diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go +index aadb1ff52b067f..89101acdbe6d73 100644 +--- a/src/cmd/dist/test.go ++++ b/src/cmd/dist/test.go +@@ -158,10 +158,12 @@ func (t *tester) run() { + } + } + ++ tags := []string{"-tags=ms_skipfipscheck"} ++ + if t.rebuild { + t.out("Building packages and commands.") + // Force rebuild the whole toolchain. +- goInstall(toolenv(), gorootBinGo, append([]string{"-a"}, toolchain...)...) ++ goInstall(toolenv(), gorootBinGo, append([]string{"-a"}, append(tags, toolchain...)...)...) + } + + if !t.listMode { +@@ -178,9 +180,9 @@ func (t *tester) run() { + // and virtualization we usually start with a clean GOCACHE, so we would + // end up rebuilding large parts of the standard library that aren't + // otherwise relevant to the actual set of packages under test. +- goInstall(toolenv(), gorootBinGo, toolchain...) +- goInstall(toolenv(), gorootBinGo, toolchain...) +- goInstall(toolenv(), gorootBinGo, toolsToInstall...) ++ goInstall(toolenv(), gorootBinGo, append(tags, toolchain...)...) ++ goInstall(toolenv(), gorootBinGo, append(tags, toolchain...)...) ++ goInstall(toolenv(), gorootBinGo, append(tags, "cmd")...) + } + } + +@@ -720,7 +722,7 @@ func (t *tester) registerTests() { + }) + + // Check that all crypto packages compile (and test correctly, in longmode) with fips. +- if t.fipsSupported() { ++ if false { // Disable these tests, they don't work if CNG/OpenSSL FIPS mode is not enabled. We already have dedicated builders for this. + // Test standard crypto packages with fips140=on. + t.registerTest("GOFIPS140=latest go test crypto/...", &goTest{ + variant: "gofips140", +@@ -774,7 +776,9 @@ func (t *tester) registerTests() { + } + + // Test ios/amd64 for the iOS simulator. +- if goos == "darwin" && goarch == "amd64" && t.cgoEnabled { ++ if goos == "darwin" && goarch == "amd64" && t.cgoEnabled && ++ // IOS is not supported with systemcrypto. ++ !systemCryptoEnabled() { + t.registerTest("GOOS=ios on darwin/amd64", + &goTest{ + variant: "amd64ios", +@@ -1023,7 +1027,9 @@ func (t *tester) registerTests() { + t.registerRaceTests() + } + +- if goos != "android" && !t.iOS() { ++ // cmd/internal/testdir uses -buildmode=exe on darwin, ++ // which is not supported by systemcrypto. ++ if goos != "android" && !t.iOS() && !systemCryptoEnabled() { + // Only start multiple test dir shards on builders, + // where they get distributed to multiple machines. + // See issues 20141 and 31834. +@@ -1380,12 +1386,11 @@ func (t *tester) registerCgoTests(heading string) { + // a C linker warning on Linux. + // in function `bio_ip_and_port_to_socket_and_addr': + // warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking +- if staticCheck.skip == nil && goos == "linux" && strings.Contains(goexperiment, "boringcrypto") { ++ if staticCheck.skip == nil && goos == "linux" && systemCryptoEnabled() { + staticCheck.skip = func(*distTest) (string, bool) { + return "skipping static linking check on Linux when using boringcrypto to avoid C linker warning about getaddrinfo", true + } + } +- + // Static linking tests + if goos != "android" && p != "netbsd/arm" && !t.msan && !t.asan { + // TODO(#56629): Why does this fail on netbsd-arm? +@@ -1908,3 +1913,28 @@ func goexperiments(exps ...string) string { + return existing + strings.Join(exps, ",") + + } ++ ++func systemCryptoEnabled() bool { ++ // Keep this in sync with internal/systemcrypto.EnabledFor. cmd/dist is built ++ // by the bootstrap toolchain before internal/systemcrypto exists, so it can't ++ // import that package until the minimum bootstrap Go version is Go 1.27. ++ if os.Getenv("MS_GO_NOSYSTEMCRYPTO") == "1" { ++ return false ++ } ++ var enabled bool ++ switch goos { ++ case "linux", "darwin": ++ enabled = true ++ case "windows": ++ enabled = goarch == "amd64" || goarch == "arm64" ++ } ++ for _, exp := range strings.Split(goexperiment, ",") { ++ switch exp { ++ case "none", "nosystemcrypto": ++ enabled = false ++ case "systemcrypto": ++ enabled = true ++ } ++ } ++ return enabled ++} +diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go +index 74cafe9613917d..31ce076273aba2 100644 +--- a/src/cmd/go/alldocs.go ++++ b/src/cmd/go/alldocs.go +@@ -2616,6 +2616,9 @@ + // mentioned will be considered insecure by 'go get'. + // Because the variable is defined by Git, the default value cannot + // be set using 'go env -w'. ++// MS_GO_NOSYSTEMCRYPTO ++// Whether to disable the use of the system's cryptography libraries. ++// Set to 1 to disable the use of system cryptography libraries. + // + // Additional information available from 'go env' but not read from the environment: + // +diff --git a/src/cmd/go/go_boring_test.go b/src/cmd/go/go_boring_test.go +index ed0fbf3d53d75b..a2f74c8095d449 100644 +--- a/src/cmd/go/go_boring_test.go ++++ b/src/cmd/go/go_boring_test.go +@@ -2,11 +2,13 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + package main_test + +-import "testing" ++import ( ++ "testing" ++) + + func TestBoringInternalLink(t *testing.T) { + tg := testgo(t) +diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go +index 47839e0229b951..99ac16a3951934 100644 +--- a/src/cmd/go/go_test.go ++++ b/src/cmd/go/go_test.go +@@ -14,6 +14,7 @@ import ( + "go/format" + "internal/godebug" + "internal/platform" ++ "internal/systemcrypto" + "internal/testenv" + "io" + "io/fs" +@@ -110,6 +111,13 @@ func TestMain(m *testing.M) { + if v := os.Getenv("TESTGO_TOOLCHAIN_VERSION"); v != "" { + work.ToolchainVersion = v + } ++ // Remove the " X:systemcrypto" suffix from the toolchain version, ++ // else it won't match the compiler version and the tests will fail. ++ // This happens because "go" invocations inside TestScript don't ++ // really call the go binary, which is never built with systemcrypto, ++ // but it instead calls the test binary with CMDGO_TEST_RUN_MAIN=1, ++ // which can be built with systemcrypto. ++ work.ToolchainVersion = strings.ReplaceAll(work.ToolchainVersion, " X:systemcrypto", "") + + if testGOROOT := os.Getenv("TESTGO_GOROOT"); testGOROOT != "" { + // Disallow installs to the GOROOT from which testgo was built. +@@ -1779,6 +1787,9 @@ func TestGenerateUsesBuildContext(t *testing.T) { + } + + func TestGoEnv(t *testing.T) { ++ if systemcrypto.Enabled() { ++ t.Skip("freebsd is not supported by system crypto") ++ } + tg := testgo(t) + tg.parallel() + defer tg.cleanup() +diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go +index 78cbbb949e7c27..f8b16160f50713 100644 +--- a/src/cmd/go/internal/cfg/cfg.go ++++ b/src/cmd/go/internal/cfg/cfg.go +@@ -14,11 +14,13 @@ import ( + "internal/buildcfg" + "internal/cfg" + "internal/platform" ++ "internal/systemcrypto" + "io" + "io/fs" + "os" + "path/filepath" + "runtime" ++ "runtime/debug" + "strings" + "sync" + "time" +@@ -229,6 +231,21 @@ func ForceHost() { + BuildContext = defaultContext() + // Call SetGOROOT to properly set the GOROOT on the new context. + SetGOROOT(Getenv("GOROOT"), false) ++ // The Microsoft build of Go might be built with non-default ++ // GOEXPERIMENT and BuildTags, so we need to get them from ++ // the build info. ++ bi, ok := debug.ReadBuildInfo() ++ if !ok { ++ panic("missing build info") ++ } ++ for _, s := range bi.Settings { ++ switch s.Key { ++ case "-tags": ++ BuildContext.BuildTags = strings.Split(s.Value, ",") ++ case "GOEXPERIMENT": ++ RawGOEXPERIMENT = s.Value ++ } ++ } + // Recompute experiments: the settings determined depend on GOOS and GOARCH. + // This will also update the BuildContext's tool tags to include the new + // experiment tags. +@@ -311,10 +328,23 @@ func computeExperiment() { + + // Add build tags based on the experiments in effect. + exps := Experiment.Enabled() +- expTags := make([]string, 0, len(exps)+len(BuildContext.ToolTags)) ++ expTags := make([]string, 0, len(exps)+len(BuildContext.ToolTags)+2) + for _, exp := range exps { + expTags = append(expTags, "goexperiment."+exp) + } ++ if systemcrypto.EnabledFor(Goos, Goarch) { ++ expTags = append(expTags, "goexperiment.systemcrypto") ++ // The per-platform crypto GOEXPERIMENT values have been removed, ++ // but their build tags remain supported for legacy source compatibility. ++ switch Goos { ++ case "darwin": ++ expTags = append(expTags, "goexperiment.darwincrypto") ++ case "linux": ++ expTags = append(expTags, "goexperiment.opensslcrypto") ++ case "windows": ++ expTags = append(expTags, "goexperiment.cngcrypto") ++ } ++ } + BuildContext.ToolTags = append(expTags, BuildContext.ToolTags...) + } + +diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go +index 3d72c368b088a2..4fe28740a6e3c2 100644 +--- a/src/cmd/go/internal/envcmd/env.go ++++ b/src/cmd/go/internal/envcmd/env.go +@@ -124,6 +124,7 @@ func MkEnv() []cfg.EnvVar { + {Name: "GOTOOLDIR", Value: build.ToolDir}, + {Name: "GOVCS", Value: cfg.GOVCS}, + {Name: "GOVERSION", Value: runtime.Version()}, ++ {Name: "MS_GO_NOSYSTEMCRYPTO", Value: os.Getenv("MS_GO_NOSYSTEMCRYPTO")}, + } + + for i := range env { +@@ -132,7 +133,7 @@ func MkEnv() []cfg.EnvVar { + if env[i].Value != "on" && env[i].Value != "" { + env[i].Changed = true + } +- case "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPACKAGESDRIVER", "GOPRIVATE", "GOTMPDIR", "GOVCS": ++ case "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPACKAGESDRIVER", "GOPRIVATE", "GOTMPDIR", "GOVCS", "MS_GO_NOSYSTEMCRYPTO": + if env[i].Value != "" { + env[i].Changed = true + } +diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go +index 4f76f7f9c1fc68..783d11609c0bf4 100644 +--- a/src/cmd/go/internal/help/helpdoc.go ++++ b/src/cmd/go/internal/help/helpdoc.go +@@ -755,6 +755,9 @@ Special-purpose environment variables: + mentioned will be considered insecure by 'go get'. + Because the variable is defined by Git, the default value cannot + be set using 'go env -w'. ++ MS_GO_NOSYSTEMCRYPTO ++ Whether to disable the use of the system's cryptography libraries. ++ Set to 1 to disable the use of system cryptography libraries. + + Additional information available from 'go env' but not read from the environment: + +diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go +index 96bf64a71ff1ed..ebfbdd0edbc3db 100644 +--- a/src/cmd/go/internal/load/pkg.go ++++ b/src/cmd/go/internal/load/pkg.go +@@ -16,6 +16,7 @@ import ( + "go/token" + "internal/godebug" + "internal/platform" ++ "internal/systemcrypto" + "io/fs" + "os" + pathpkg "path" +@@ -2448,6 +2449,9 @@ func (p *Package) setBuildInfo(ctx context.Context, f *modfetch.Fetcher, autoVCS + buildmode = "archive" + } + } ++ if systemcrypto.EnabledFor(cfg.Goos, cfg.Goarch) { ++ appendSetting("microsoft_systemcrypto", "1") ++ } + appendSetting("-buildmode", buildmode) + appendSetting("-compiler", cfg.BuildContext.Compiler) + if gccgoflags := BuildGccgoflags.String(); gccgoflags != "" && cfg.BuildContext.Compiler == "gccgo" { +diff --git a/src/cmd/go/internal/modindex/build_test.go b/src/cmd/go/internal/modindex/build_test.go +new file mode 100644 +index 00000000000000..1756c5d027fee0 +--- /dev/null ++++ b/src/cmd/go/internal/modindex/build_test.go +@@ -0,0 +1,73 @@ ++// Copyright 2023 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++// This file is not a copy. It tests that the copied code in this directory is ++// maintained. Specifically, it tests areas that are modified by microsoft/go. ++// The files also contain intentional modifications, so it isn't reasonable (as ++// of writing) to test that the entire file is identical. ++ ++package modindex ++ ++import ( ++ "flag" ++ "os" ++ "strings" ++ "testing" ++) ++ ++var fixCopy = flag.Bool("fixcopy", false, "if true, update some copied code in build.go") ++ ++func TestCopyIdentical(t *testing.T) { ++ originalBytes, err := os.ReadFile("../../../../go/build/build.go") ++ if err != nil { ++ t.Fatal(err) ++ } ++ wantCode := string(originalBytes) ++ ++ gotBytes, err := os.ReadFile("build.go") ++ if err != nil { ++ t.Fatal(err) ++ } ++ gotCode := string(gotBytes) ++ ++ tests := []struct { ++ name string ++ prefix string ++ suffix string ++ }{ ++ {"matchTag", "func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {", "\n}"}, ++ } ++ for _, tt := range tests { ++ t.Run(tt.name, func(t *testing.T) { ++ var want, got string ++ if _, after, ok := strings.Cut(wantCode, tt.prefix); ok { ++ if before, _, ok := strings.Cut(after, tt.suffix); ok { ++ want = before ++ } else { ++ t.Fatal("suffix not found in original file") ++ } ++ } else { ++ t.Fatal("prefix not found in original file") ++ } ++ if _, after, ok := strings.Cut(gotCode, tt.prefix); ok { ++ if before, _, ok := strings.Cut(after, tt.suffix); ok { ++ got = before ++ } else { ++ t.Fatal("suffix not found in copied file") ++ } ++ } else { ++ t.Fatal("prefix not found in copied file") ++ } ++ if got != want { ++ if *fixCopy { ++ if err := os.WriteFile("build.go", []byte(strings.Replace(gotCode, got, want, 1)), 0o666); err != nil { ++ t.Fatal(err) ++ } ++ } else { ++ t.Error("copy is not the same as original; use '-fixcopy' to replace copied code with the code from the original file") ++ } ++ } ++ }) ++ } ++} +diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go +index 97c27c8caa44d6..bb18bb112a314a 100644 +--- a/src/cmd/go/internal/tool/tool.go ++++ b/src/cmd/go/internal/tool/tool.go +@@ -335,6 +335,10 @@ func buildAndRunBuiltinTool(ld *modload.Loader, ctx context.Context, toolName, t + // Override GOOS and GOARCH for the build to build the tool using + // the same GOOS and GOARCH as this go command. + cfg.ForceHost() ++ // The tool might build some additional packages, set the GOEXPERIMENT ++ // environment variable to the one we just computed in cfg.ForceHost. ++ originalGOEXPERIMENT := os.Getenv("GOEXPERIMENT") ++ os.Setenv("GOEXPERIMENT", cfg.RawGOEXPERIMENT) + + // Ignore go.mod and go.work: we don't need them, and we want to be able + // to run the tool even if there's an issue with the module or workspace the +@@ -342,8 +346,11 @@ func buildAndRunBuiltinTool(ld *modload.Loader, ctx context.Context, toolName, t + ld.RootMode = modload.NoRoot + + runFunc := func(b *work.Builder, ctx context.Context, a *work.Action) error { ++ // Now that the tool is built, revert the GOEXPERIMENT ++ // environment variable to the original value. ++ env := append(os.Environ(), "GOEXPERIMENT="+originalGOEXPERIMENT) + cmdline := str.StringList(builtTool(a), a.Args) +- return runBuiltTool(toolName, nil, cmdline) ++ return runBuiltTool(toolName, env, cmdline) + } + + buildAndRunTool(ld, ctx, tool, args, runFunc) +diff --git a/src/cmd/go/internal/verylongtest/testdata/script/README b/src/cmd/go/internal/verylongtest/testdata/script/README +index 9ec997a138f0f2..09c4e7955ec162 100644 +--- a/src/cmd/go/internal/verylongtest/testdata/script/README ++++ b/src/cmd/go/internal/verylongtest/testdata/script/README +@@ -281,6 +281,8 @@ The available conditions are: + testing.Short() + [symlink] + testenv.HasSymlink() ++[systemcrypto] ++ system crypto is enabled + [verbose] + testing.Verbose() + +diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go +index 088b9a8b5fdb2e..4e1e545367afef 100644 +--- a/src/cmd/go/script_test.go ++++ b/src/cmd/go/script_test.go +@@ -299,6 +299,9 @@ var extraEnvKeys = []string{ + "GO_TESTING_GOTOOLS", // for gccgo testing + "GCCGO", // for gccgo testing + "GCCGOTOOLDIR", // for gccgo testing ++ "MS_GO_NOSYSTEMCRYPTO", ++ ++ "OPENSSL_FORCE_FIPS_MODE", // useful for testing on Mariner 2. + } + + // updateSum runs 'go mod tidy', 'go list -mod=mod -m all', or +diff --git a/src/cmd/go/systemcrypto_test.go b/src/cmd/go/systemcrypto_test.go +new file mode 100644 +index 00000000000000..eeb0ee9c3c9396 +--- /dev/null ++++ b/src/cmd/go/systemcrypto_test.go +@@ -0,0 +1,272 @@ ++// Copyright 2025 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package main_test ++ ++import ( ++ "fmt" ++ "internal/testenv" ++ "os" ++ "path/filepath" ++ "strings" ++ "testing" ++) ++ ++const fileWithCrypto = ` ++package main ++ ++import ( ++ "crypto/sha256" ++ "fmt" ++) ++ ++func main() { ++ fmt.Println(sha256.Sum256([]byte("Hello, World!"))) ++} ++` ++ ++const fileWithoutCrypto = ` ++package main ++ ++import ( ++ "fmt" ++) ++ ++func main() { ++ fmt.Println("Hello, World!") ++} ++` ++ ++func execGoTool(t *testing.T, allowFail bool, env []string, args ...string) (string, bool) { ++ return execGoToolInDir(t, "", allowFail, env, args...) ++} ++ ++func execGoToolInDir(t *testing.T, dir string, allowFail bool, env []string, args ...string) (string, bool) { ++ t.Helper() ++ cmd := testenv.Command(t, testenv.GoToolPath(t), args...) ++ cmd = testenv.CleanCmdEnv(cmd) ++ cmd.Dir = dir ++ cmd.Env = append(cmd.Env, env...) ++ out, err := cmd.CombinedOutput() ++ sout := strings.TrimSpace(string(out)) ++ if err != nil { ++ if allowFail { ++ return sout, false ++ } ++ t.Fatalf("go build failed: %v\n%s", err, out) ++ } ++ return sout, true ++} ++ ++// writeFile creates a temporary file with the given content and returns its path. ++func writeFile(t *testing.T, content string) string { ++ t.Helper() ++ name := "main.go" ++ if strings.Contains(content, "testing") { ++ name = "main_test.go" ++ } ++ name = filepath.Join(t.TempDir(), name) ++ if err := os.WriteFile(name, []byte(content), 0o644); err != nil { ++ t.Fatal(err) ++ } ++ return name ++} ++ ++func outPath(t *testing.T) string { ++ t.Helper() ++ return filepath.Join(t.TempDir(), "out") ++} ++ ++func TestSystemCryptoNoCgoChecks(t *testing.T) { ++ t.Parallel() ++ ++ cryptoFile := writeFile(t, fileWithCrypto) ++ ++ tt := []struct { ++ goos string ++ goarch string ++ }{ ++ {"linux", "386"}, ++ {"linux", "amd64"}, ++ {"linux", "arm64"}, ++ {"linux", "loong64"}, ++ {"linux", "ppc64le"}, ++ {"linux", "riscv64"}, ++ {"linux", "s390x"}, ++ {"linux", "arm"}, ++ {"darwin", "amd64"}, ++ {"darwin", "arm64"}, ++ {"windows", "386"}, ++ {"windows", "amd64"}, ++ {"windows", "arm64"}, ++ } ++ for _, tc := range tt { ++ t.Run(tc.goos+"/"+tc.goarch, func(t *testing.T) { ++ t.Parallel() ++ env := []string{"CGO_ENABLED=0", "GOOS=" + tc.goos, "GOARCH=" + tc.goarch, "MS_GO_NOSYSTEMCRYPTO=0"} ++ if out, ok := execGoTool(t, true, env, "build", "-o", outPath(t), cryptoFile); !ok { ++ t.Fatalf("expected success, got failure: %s", out) ++ } ++ }) ++ } ++} ++ ++func TestSystemCryptoFIPS(t *testing.T) { ++ // Test different go commands with GODEBUG=fips140=on ++ // to exercise the ms_skipfipscheck build tag. ++ t.Parallel() ++ env := []string{"GODEBUG=fips140=on"} ++ ++ cryptoFile := writeFile(t, fileWithCrypto) ++ nonCryptoFile := writeFile(t, fileWithoutCrypto) ++ ++ // Build should always succeed given that the go toolchain ++ // is built with the ms_skipfipscheck build tag. ++ execGoTool(t, false, env, "build", "-o", outPath(t), cryptoFile) ++ execGoTool(t, false, env, "build", "-o", outPath(t), nonCryptoFile) ++ ++ // Run should always succeed if the target go package ++ // doesn't use crypto. ++ execGoTool(t, false, env, "run", nonCryptoFile) ++ ++ // Run may or may not fail if the target go package uses crypto, ++ // because while the toolchain was built with ms_skipfipscheck, the ++ // target program was not. Failure depends on system crypto mode ++ // and presence of system-provided crypto, and it can't be tested here. ++} ++ ++func TestSystemCryptoDefault(t *testing.T) { ++ t.Parallel() ++ cryptoFile := writeFile(t, fileWithCrypto) ++ ++ // Add here all the OS/ARCH combinations that enable systemcrypto by default. ++ type testCase struct { ++ goos string ++ goarch string ++ } ++ test := []testCase{ ++ {"linux", "amd64"}, ++ {"linux", "arm64"}, ++ {"darwin", "amd64"}, ++ {"darwin", "arm64"}, ++ {"windows", "amd64"}, ++ {"windows", "arm64"}, ++ } ++ for _, tt := range test { ++ t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), func(t *testing.T) { ++ t.Parallel() ++ out := outPath(t) ++ env := []string{"CGO_ENABLED=0", "GOOS=" + tt.goos, "GOARCH=" + tt.goarch, "MS_GO_NOSYSTEMCRYPTO=0"} ++ execGoTool(t, false, env, "build", "-o", out, cryptoFile) ++ // Check that the binary has the correct settings. ++ settings, _ := execGoTool(t, false, nil, "version", "-m", out) ++ if !strings.Contains(settings, "microsoft_systemcrypto=1") { ++ t.Errorf("expected microsoft_systemcrypto=1 in settings, got %v", settings) ++ } ++ }) ++ } ++} ++ ++func TestSystemCryptoDisabled(t *testing.T) { ++ t.Parallel() ++ cryptoFile := writeFile(t, fileWithCrypto) ++ ++ tests := []struct { ++ goos string ++ goarch string ++ }{ ++ {"linux", "amd64"}, ++ {"linux", "arm64"}, ++ {"darwin", "amd64"}, ++ {"darwin", "arm64"}, ++ {"windows", "amd64"}, ++ {"windows", "arm64"}, ++ } ++ for _, tt := range tests { ++ t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), func(t *testing.T) { ++ t.Parallel() ++ out := outPath(t) ++ env := []string{"CGO_ENABLED=0", "GOOS=" + tt.goos, "GOARCH=" + tt.goarch, "MS_GO_NOSYSTEMCRYPTO=1"} ++ execGoTool(t, false, env, "build", "-o", out, cryptoFile) ++ settings, _ := execGoTool(t, false, nil, "version", "-m", out) ++ if strings.Contains(settings, "microsoft_systemcrypto=1") { ++ t.Errorf("expected microsoft_systemcrypto=1 not to be in settings, got %v", settings) ++ } ++ }) ++ } ++} ++ ++func TestSystemCryptoBuildTag(t *testing.T) { ++ t.Parallel() ++ ++ dir := t.TempDir() ++ files := map[string]string{ ++ "go.mod": "module example.com/systemcrypto-tag\n\ngo 1.27\n", ++ "always.go": "package main\n", ++ "with_system.go": "//go:build goexperiment.systemcrypto\n\npackage main\n", ++ "with_openssl.go": "//go:build goexperiment.opensslcrypto\n\npackage main\n", ++ "with_cng.go": "//go:build goexperiment.cngcrypto\n\npackage main\n", ++ "with_darwin.go": "//go:build goexperiment.darwincrypto\n\npackage main\n", ++ "without_system.go": "//go:build !goexperiment.systemcrypto\n\npackage main\n", ++ } ++ for name, content := range files { ++ if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil { ++ t.Fatal(err) ++ } ++ } ++ ++ tests := []struct { ++ name string ++ env []string ++ wantFiles []string ++ badFiles []string ++ }{ ++ { ++ name: "linux_supported", ++ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFiles: []string{"with_system.go", "with_openssl.go"}, ++ badFiles: []string{"without_system.go", "with_cng.go", "with_darwin.go"}, ++ }, ++ { ++ name: "darwin_supported", ++ env: []string{"GOOS=darwin", "GOARCH=arm64", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFiles: []string{"with_system.go", "with_darwin.go"}, ++ badFiles: []string{"without_system.go", "with_openssl.go", "with_cng.go"}, ++ }, ++ { ++ name: "windows_supported", ++ env: []string{"GOOS=windows", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFiles: []string{"with_system.go", "with_cng.go"}, ++ badFiles: []string{"without_system.go", "with_openssl.go", "with_darwin.go"}, ++ }, ++ { ++ name: "disabled", ++ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=1"}, ++ wantFiles: []string{"without_system.go"}, ++ badFiles: []string{"with_system.go", "with_openssl.go", "with_cng.go", "with_darwin.go"}, ++ }, ++ { ++ name: "unsupported", ++ env: []string{"GOOS=windows", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFiles: []string{"without_system.go"}, ++ badFiles: []string{"with_system.go", "with_openssl.go", "with_cng.go", "with_darwin.go"}, ++ }, ++ } ++ for _, tt := range tests { ++ t.Run(tt.name, func(t *testing.T) { ++ t.Parallel() ++ out, _ := execGoToolInDir(t, dir, false, tt.env, "list", "-f", "{{.GoFiles}}", ".") ++ for _, wantFile := range tt.wantFiles { ++ if !strings.Contains(out, wantFile) { ++ t.Fatalf("go list .GoFiles = %q, want %s", out, wantFile) ++ } ++ } ++ for _, badFile := range tt.badFiles { ++ if strings.Contains(out, badFile) { ++ t.Fatalf("go list .GoFiles = %q, did not want %s", out, badFile) ++ } ++ } ++ }) ++ } ++} +diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README +index 9fa2c92c0b32b2..c0dd9e533e8e48 100644 +--- a/src/cmd/go/testdata/script/README ++++ b/src/cmd/go/testdata/script/README +@@ -421,6 +421,8 @@ The available conditions are: + testing.Short() + [symlink] + testenv.HasSymlink() ++[systemcrypto] ++ system crypto is enabled + [trimpath] + test binary was built with -trimpath + [verbose] +diff --git a/src/cmd/go/testdata/script/darwin_no_cgo.txt b/src/cmd/go/testdata/script/darwin_no_cgo.txt +index fa445925b7c374..248be5160833c1 100644 +--- a/src/cmd/go/testdata/script/darwin_no_cgo.txt ++++ b/src/cmd/go/testdata/script/darwin_no_cgo.txt +@@ -4,6 +4,7 @@ + # of cmd/go, which imports approximately everything + # in std (certainly everything relevant). + [!GOOS:darwin] skip ++[systemcrypto] skip + go list -deps cmd/go + ! stdout runtime/cgo + +diff --git a/src/cmd/go/testdata/script/env_changed.txt b/src/cmd/go/testdata/script/env_changed.txt +index da9560062704e6..a12836ff65fd03 100644 +--- a/src/cmd/go/testdata/script/env_changed.txt ++++ b/src/cmd/go/testdata/script/env_changed.txt +@@ -13,6 +13,7 @@ env CGO_CPPFLAGS=nodefault + env GOFIPS140=latest + [cgo] env CGO_ENABLED=0 + env GCCGO=nodefault ++env MS_GO_NOSYSTEMCRYPTO=1 + + go env -changed + # linux output like GOTOOLCHAIN='local' +@@ -26,6 +27,7 @@ stdout 'CGO_CPPFLAGS=''?nodefault''?' + stdout 'GOFIPS140=''?latest''?' + [cgo] stdout 'CGO_ENABLED=''?0''?' + stdout 'GCCGO=''?nodefault''?' ++stdout 'MS_GO_NOSYSTEMCRYPTO=''?1''?' + + go env -changed -json + stdout '"GOTOOLCHAIN": "local"' +@@ -37,6 +39,7 @@ stdout '"CGO_CPPFLAGS": "nodefault"' + stdout '"GOFIPS140": "latest"' + [cgo] stdout '"CGO_ENABLED": "0"' + stdout '"GCCGO": "nodefault"' ++stdout '"MS_GO_NOSYSTEMCRYPTO": "1"' + + [GOOS:windows] env GOOS=linux + [!GOOS:windows] env GOOS=windows +diff --git a/src/cmd/go/testdata/script/env_cross_build.txt b/src/cmd/go/testdata/script/env_cross_build.txt +index 91d1cb936d3652..cca75b4be873d0 100644 +--- a/src/cmd/go/testdata/script/env_cross_build.txt ++++ b/src/cmd/go/testdata/script/env_cross_build.txt +@@ -1,6 +1,8 @@ + # Test that the correct default GOEXPERIMENT is used when cross + # building with GOENV (#46815). + ++[systemcrypto] skip # systemcrypto is not supported on ios nor mips ++ + # Unset variables set by the TestScript harness. Users typically won't + # explicitly configure these, and #46815 doesn't repro if they are. + env GOOS= +diff --git a/src/cmd/go/testdata/script/gopath_std_vendor.txt b/src/cmd/go/testdata/script/gopath_std_vendor.txt +index 4aaf46b5d0f0dc..46d03fcd4aeae3 100644 +--- a/src/cmd/go/testdata/script/gopath_std_vendor.txt ++++ b/src/cmd/go/testdata/script/gopath_std_vendor.txt +@@ -1,5 +1,14 @@ + env GO111MODULE=off + ++# Disable systemcrypto while evaluating test dependencies to avoid importing ++# vendored crypto module dependencies like go-crypto-openssl. This test script ++# is not set up to handle any vendored libraries being imported other than ++# golang.org/x/net/http2/hpack, so we must make sure it is the only one. ++# ++# See https://github.com/microsoft/go/issues/481 for more details, such as the ++# dependency chain that would cause the failure if systemcrypto isn't disabled. ++env MS_GO_NOSYSTEMCRYPTO=1 ++ + [!compiler:gc] skip + + go list -f '{{.Dir}}' vendor/golang.org/x/net/http2/hpack +diff --git a/src/cmd/go/testdata/script/test_android_issue62123.txt b/src/cmd/go/testdata/script/test_android_issue62123.txt +index 2f46a6b44bffe1..b036dde70ed36e 100644 +--- a/src/cmd/go/testdata/script/test_android_issue62123.txt ++++ b/src/cmd/go/testdata/script/test_android_issue62123.txt +@@ -1,3 +1,5 @@ ++[systemcrypto] skip # systemcrypto is not supported on android ++ + env GOOS=android GOARCH=amd64 CGO_ENABLED=0 + + ! go build -o $devnull cmd/buildid +diff --git a/src/cmd/internal/fuzztest/testdata/script/README b/src/cmd/internal/fuzztest/testdata/script/README +index 9ec997a138f0f2..09c4e7955ec162 100644 +--- a/src/cmd/internal/fuzztest/testdata/script/README ++++ b/src/cmd/internal/fuzztest/testdata/script/README +@@ -281,6 +281,8 @@ The available conditions are: + testing.Short() + [symlink] + testenv.HasSymlink() ++[systemcrypto] ++ system crypto is enabled + [verbose] + testing.Verbose() + +diff --git a/src/cmd/internal/script/scripttest/conditions.go b/src/cmd/internal/script/scripttest/conditions.go +index 6702e9279b106b..09b92a3ed98df1 100644 +--- a/src/cmd/internal/script/scripttest/conditions.go ++++ b/src/cmd/internal/script/scripttest/conditions.go +@@ -9,6 +9,7 @@ import ( + "fmt" + "internal/buildcfg" + "internal/platform" ++ "internal/systemcrypto" + "internal/testenv" + "runtime" + "strings" +@@ -48,6 +49,7 @@ func AddToolChainScriptConditions(t *testing.T, conds map[string]script.Cond, go + add("mustlinkext", script.Condition("platform always requires external linking", mustLinkExt)) + add("pielinkext", script.Condition("platform requires external linking for PIE", pieLinkExt)) + add("race", sysCondition("-race", platform.RaceDetectorSupported, true, goHostOS, goHostArch)) ++ add("systemcrypto", script.Condition("system crypto is enabled", hasSystemCrypto)) + add("symlink", lazyBool("testenv.HasSymlink()", testenv.HasSymlink)) + } + +@@ -96,10 +98,16 @@ func hasGodebug(s *script.State, value string) (bool, error) { + return false, nil + } + +-func hasGoexperiment(s *script.State, value string) (bool, error) { ++func hasSystemCrypto(s *script.State) (bool, error) { + GOOS, _ := s.LookupEnv("GOOS") + GOARCH, _ := s.LookupEnv("GOARCH") ++ return systemcrypto.EnabledFor(GOOS, GOARCH), nil ++} ++ ++func hasGoexperiment(s *script.State, value string) (bool, error) { + goexp, _ := s.LookupEnv("GOEXPERIMENT") ++ GOOS, _ := s.LookupEnv("GOOS") ++ GOARCH, _ := s.LookupEnv("GOARCH") + flags, err := buildcfg.ParseGOEXPERIMENT(GOOS, GOARCH, goexp) + if err != nil { + return false, err +diff --git a/src/cmd/internal/testdir/testdir_test.go b/src/cmd/internal/testdir/testdir_test.go +index d81b3a849cb975..f051b6dfa209dd 100644 +--- a/src/cmd/internal/testdir/testdir_test.go ++++ b/src/cmd/internal/testdir/testdir_test.go +@@ -14,6 +14,7 @@ import ( + "go/build" + "go/build/constraint" + "hash/fnv" ++ "internal/systemcrypto" + "internal/testenv" + "io" + "io/fs" +@@ -118,6 +119,11 @@ func Test(t *testing.T) { + goDebug = env.GODEBUG + tmpDir = t.TempDir() + ++ if systemcrypto.EnabledFor(goos, goarch) { ++ // Codegen tests cross-compile to platforms that do not support system crypto. ++ *allCodegen = false ++ } ++ + common := testCommon{ + gorootTestDir: filepath.Join(testenv.GOROOT(t), "test"), + runoutputGate: make(chan bool, *runoutputLimit), +diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go +index acbebf28598a9c..6ef32735ddfd57 100644 +--- a/src/cmd/link/internal/ld/config.go ++++ b/src/cmd/link/internal/ld/config.go +@@ -34,6 +34,13 @@ func (mode *BuildMode) Set(s string) error { + return fmt.Errorf("invalid buildmode: %q", s) + case "exe": + switch buildcfg.GOOS + "/" + buildcfg.GOARCH { ++ case "darwin/amd64": ++ // We can't link against the static object file when using no_pie ++ if platform.SystemCryptoSupported(buildcfg.GOOS, buildcfg.GOARCH) && !buildcfg.SystemCryptoDisabled { ++ *mode = BuildModePIE ++ } else { ++ *mode = BuildModeExe ++ } + case "darwin/arm64", "windows/arm64": // On these platforms, everything is PIE + *mode = BuildModePIE + default: +diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go +index 9cbc12919f1d00..e64a9c5299e95a 100644 +--- a/src/cmd/link/internal/ld/lib.go ++++ b/src/cmd/link/internal/ld/lib.go +@@ -1177,6 +1177,12 @@ var hostobj []Hostobj + // These packages can use internal linking mode. + // Others trigger external mode. + var internalpkg = []string{ ++ "vendor/github.com/microsoft/go-crypto-darwin/internal/cryptokit", ++ "vendor/github.com/microsoft/go-crypto-darwin/internal/commoncrypto", ++ "vendor/github.com/microsoft/go-crypto-darwin/internal/security", ++ "vendor/github.com/microsoft/go-crypto-openssl/internal/ossl", ++ "vendor/github.com/microsoft/go-crypto-openssl/osslsetup", ++ "vendor/github.com/microsoft/go-crypto-openssl/openssl", + "crypto/internal/boring", + "crypto/internal/boring/syso", + "crypto/x509", +diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go +index e7c406c4ad1eb8..7574d1b069807d 100644 +--- a/src/cmd/link/link_test.go ++++ b/src/cmd/link/link_test.go +@@ -14,6 +14,7 @@ import ( + "internal/abi" + "internal/buildcfg" + "internal/platform" ++ "internal/systemcrypto" + "internal/testenv" + "internal/xcoff" + "os" +@@ -368,6 +369,9 @@ func TestBuildForTvOS(t *testing.T) { + if runtime.GOOS != "darwin" { + t.Skip("skipping on non-darwin platform") + } ++ if systemcrypto.Enabled() { ++ t.Skip("tvOS is not supported by system crypto") ++ } + if testing.Short() && testenv.Builder() == "" { + t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty") + } +@@ -598,6 +602,10 @@ func TestIssue34788Android386TLSSequence(t *testing.T) { + t.Skip("skipping on non-{linux,darwin}/amd64 platform") + } + ++ if systemcrypto.Enabled() { ++ t.Skip("skipping on system crypto, which does not support android/386") ++ } ++ + t.Parallel() + + tmpdir := t.TempDir() +diff --git a/src/cmd/link/testdata/script/README b/src/cmd/link/testdata/script/README +index 9ec997a138f0f2..09c4e7955ec162 100644 +--- a/src/cmd/link/testdata/script/README ++++ b/src/cmd/link/testdata/script/README +@@ -281,6 +281,8 @@ The available conditions are: + testing.Short() + [symlink] + testenv.HasSymlink() ++[systemcrypto] ++ system crypto is enabled + [verbose] + testing.Verbose() + +diff --git a/src/crypto/aes/aes.go b/src/crypto/aes/aes.go +index 22ea8819ed239a..1e2cba08c1c760 100644 +--- a/src/crypto/aes/aes.go ++++ b/src/crypto/aes/aes.go +@@ -15,7 +15,7 @@ package aes + + import ( + "crypto/cipher" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/aes" + "strconv" + ) +diff --git a/src/crypto/aes/aes_test.go b/src/crypto/aes/aes_test.go +index cfe75f4057a96b..19a5e7d757ca8c 100644 +--- a/src/crypto/aes/aes_test.go ++++ b/src/crypto/aes/aes_test.go +@@ -5,7 +5,7 @@ + package aes + + import ( +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "fmt" + "testing" +diff --git a/src/crypto/boring/boring.go b/src/crypto/boring/boring.go +index 097c37e343fdb8..a5d603896d3890 100644 +--- a/src/crypto/boring/boring.go ++++ b/src/crypto/boring/boring.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + // Package boring exposes functions that are only available when building with + // Go+BoringCrypto. This package is available on all targets as long as the +@@ -13,7 +13,7 @@ + // is satisfied, so that applications can tag files that use this package. + package boring + +-import "crypto/internal/boring" ++import boring "crypto/internal/backend" + + // Enabled reports whether BoringCrypto handles supported crypto operations. + func Enabled() bool { +diff --git a/src/crypto/cipher/cbc.go b/src/crypto/cipher/cbc.go +index 87bafee08ade4f..198de1d17b7e7c 100644 +--- a/src/crypto/cipher/cbc.go ++++ b/src/crypto/cipher/cbc.go +@@ -44,6 +44,12 @@ type cbcEncAble interface { + NewCBCEncrypter(iv []byte) BlockMode + } + ++// fipsCBCEncAble is an interface implemented by ciphers that have a specific ++// optimized implementation of CBC encryption that is compliant with FIPS 140-2. ++type fipsCBCEncAble interface { ++ NewFIPSCBCEncrypter(iv []byte) BlockMode ++} ++ + // NewCBCEncrypter returns a BlockMode which encrypts in cipher block chaining + // mode, using the given Block. The length of iv must be the same as the + // Block's block size. +@@ -54,6 +60,9 @@ func NewCBCEncrypter(b Block, iv []byte) BlockMode { + if b, ok := b.(*aes.Block); ok { + return aes.NewCBCEncrypter(b, [16]byte(iv)) + } ++ if cbc, ok := b.(fipsCBCEncAble); ok { ++ return cbc.NewFIPSCBCEncrypter(iv) ++ } + if fips140only.Enforced() { + panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") + } +@@ -123,6 +132,10 @@ type cbcDecAble interface { + NewCBCDecrypter(iv []byte) BlockMode + } + ++type fipsCBCDecAble interface { ++ NewFIPSCBCDecrypter(iv []byte) BlockMode ++} ++ + // NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining + // mode, using the given Block. The length of iv must be the same as the + // Block's block size and must match the iv used to encrypt the data. +@@ -133,6 +146,9 @@ func NewCBCDecrypter(b Block, iv []byte) BlockMode { + if b, ok := b.(*aes.Block); ok { + return aes.NewCBCDecrypter(b, [16]byte(iv)) + } ++ if cbc, ok := b.(fipsCBCDecAble); ok { ++ return cbc.NewFIPSCBCDecrypter(iv) ++ } + if fips140only.Enforced() { + panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") + } +diff --git a/src/crypto/cipher/ctr.go b/src/crypto/cipher/ctr.go +index 8e63ed7e668a4c..10ed72d2540b6e 100644 +--- a/src/crypto/cipher/ctr.go ++++ b/src/crypto/cipher/ctr.go +@@ -36,12 +36,19 @@ type ctrAble interface { + NewCTR(iv []byte) Stream + } + ++type fipsCTRAble interface { ++ NewFIPSCTR(iv []byte) Stream ++} ++ + // NewCTR returns a [Stream] which encrypts/decrypts using the given [Block] in + // counter mode. The length of iv must be the same as the [Block]'s block size. + func NewCTR(block Block, iv []byte) Stream { + if block, ok := block.(*aes.Block); ok { + return aesCtrWrapper{aes.NewCTR(block, iv)} + } ++ if ctr, ok := block.(fipsCTRAble); ok { ++ return ctr.NewFIPSCTR(iv) ++ } + if fips140only.Enforced() { + panic("crypto/cipher: use of CTR with non-AES ciphers is not allowed in FIPS 140-only mode") + } +diff --git a/src/crypto/cipher/ctr_aes_test.go b/src/crypto/cipher/ctr_aes_test.go +index b6620d182fefc7..d89be99cf57545 100644 +--- a/src/crypto/cipher/ctr_aes_test.go ++++ b/src/crypto/cipher/ctr_aes_test.go +@@ -14,7 +14,7 @@ import ( + "bytes" + "crypto/aes" + "crypto/cipher" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + fipsaes "crypto/internal/fips140/aes" + "encoding/binary" +diff --git a/src/crypto/cipher/gcm.go b/src/crypto/cipher/gcm.go +index f2cf6248dc8c9b..663d418c4e5631 100644 +--- a/src/crypto/cipher/gcm.go ++++ b/src/crypto/cipher/gcm.go +@@ -89,6 +89,15 @@ func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) { + func NewGCMWithRandomNonce(cipher Block) (AEAD, error) { + c, ok := cipher.(*aes.Block) + if !ok { ++ if cipher, ok := cipher.(gcmAble); ok { ++ g, err := cipher.NewGCM(gcmStandardNonceSize, gcmTagSize) ++ if err != nil { ++ return nil, err ++ } ++ if _, ok := g.(randomNonceSealer); ok { ++ return gcmWithRandomNonce{g}, nil ++ } ++ } + return nil, errors.New("cipher: NewGCMWithRandomNonce requires aes.Block") + } + g, err := gcm.New(c, gcmStandardNonceSize, gcmTagSize) +@@ -99,7 +108,7 @@ func NewGCMWithRandomNonce(cipher Block) (AEAD, error) { + } + + type gcmWithRandomNonce struct { +- *gcm.GCM ++ aead AEAD + } + + func (g gcmWithRandomNonce) NonceSize() int { +@@ -153,7 +162,14 @@ func (g gcmWithRandomNonce) Seal(dst, nonce, plaintext, additionalData []byte) [ + plaintext = ciphertext[:len(plaintext)] + } + +- gcm.SealWithRandomNonce(g.GCM, nonce, ciphertext, plaintext, additionalData) ++ if aeadGCM, ok := g.aead.(*gcm.GCM); ok { ++ gcm.SealWithRandomNonce(aeadGCM, nonce, ciphertext, plaintext, additionalData) ++ } else if aeadGCM, ok := g.aead.(randomNonceSealer); ok { ++ aeadGCM.SealWithRandomNonce(ciphertext, nonce, plaintext, additionalData) ++ } else { ++ panic("cipher: NewGCMWithRandomNonce requires aes.Block") ++ } ++ + return ret + } + +@@ -186,7 +202,7 @@ func (g gcmWithRandomNonce) Open(dst, nonce, ciphertext, additionalData []byte) + ciphertext = ciphertext[gcmStandardNonceSize:] + } + +- _, err := g.GCM.Open(out[:0], nonce, ciphertext, additionalData) ++ _, err := g.aead.Open(out[:0], nonce, ciphertext, additionalData) + if err != nil { + return nil, err + } +@@ -200,6 +216,14 @@ type gcmAble interface { + NewGCM(nonceSize, tagSize int) (AEAD, error) + } + ++// randomNonceSealer is an interface for AEAD implementations that support ++// sealing messages using a randomly generated nonce. ++// It is used by NewGCMWithRandomNonce to determine whether the AEAD instance ++// provides its own implementation of SealWithRandomNonce. ++type randomNonceSealer interface { ++ SealWithRandomNonce(out, nonce, plaintext, additionalData []byte) ++} ++ + func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) { + if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize { + return nil, errors.New("cipher: incorrect tag size given to GCM") +@@ -208,7 +232,16 @@ func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) { + return nil, errors.New("cipher: the nonce can't have zero length") + } + if cipher, ok := cipher.(gcmAble); ok { +- return cipher.NewGCM(nonceSize, tagSize) ++ g, err := cipher.NewGCM(nonceSize, tagSize) ++ if err != nil { ++ return nil, err ++ } ++ ++ if _, ok := g.(randomNonceSealer); ok { ++ return customGCMWrapper{g}, nil ++ } ++ ++ return g, err + } + if cipher.BlockSize() != gcmBlockSize { + return nil, errors.New("cipher: NewGCM requires 128-bit block cipher") +@@ -216,6 +249,24 @@ func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) { + return &gcmFallback{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize}, nil + } + ++// customGCMWrapper wraps an AEAD implementation to expose only the AEAD interface methods. ++// This prevents extra exported methods from causing test failures in cryptotest.NoExtraMethods. ++type customGCMWrapper struct { ++ aead AEAD ++} ++ ++func (g customGCMWrapper) NonceSize() int { return g.aead.NonceSize() } ++ ++func (g customGCMWrapper) Overhead() int { return g.aead.Overhead() } ++ ++func (g customGCMWrapper) Seal(dst, nonce, plaintext, additionalData []byte) []byte { ++ return g.aead.Seal(dst, nonce, plaintext, additionalData) ++} ++ ++func (g customGCMWrapper) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { ++ return g.aead.Open(dst, nonce, ciphertext, additionalData) ++} ++ + // gcmFallback is only used for non-AES ciphers, which regrettably we + // theoretically support. It's a copy of the generic implementation from + // crypto/internal/fips140/aes/gcm/gcm_generic.go, refer to that file for more details. +diff --git a/src/crypto/cipher/gcm_test.go b/src/crypto/cipher/gcm_test.go +index 8ba0bb3981319d..7b212f8f19b101 100644 +--- a/src/crypto/cipher/gcm_test.go ++++ b/src/crypto/cipher/gcm_test.go +@@ -8,7 +8,6 @@ import ( + "bytes" + "crypto/aes" + "crypto/cipher" +- "crypto/internal/boring" + "crypto/internal/cryptotest" + "crypto/internal/fips140" + fipsaes "crypto/internal/fips140/aes" +@@ -727,9 +726,13 @@ func testGCMAEAD(t *testing.T, newCipher func(key []byte) cipher.Block) { + + // Test NewGCMWithRandomNonce. + t.Run("GCMWithRandomNonce", func(t *testing.T) { +- if _, ok := block.(*wrapper); ok || boring.Enabled { ++ if _, ok := block.(*wrapper); ok { + t.Skip("NewGCMWithRandomNonce requires an AES block cipher") + } ++ a, _ := cipher.NewGCMWithRandomNonce(block) ++ if a.NonceSize() != 0 { ++ t.Errorf("NewGCMWithRandomNonce returned AEAD with non-zero nonce size: %d", a.NonceSize()) ++ } + cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCMWithRandomNonce(block) }) + }) + }) +@@ -752,7 +755,7 @@ func TestGCMExtraMethods(t *testing.T) { + }) + t.Run("NewGCMWithRandomNonce", func(t *testing.T) { + block := newCipher(make([]byte, 16)) +- if _, ok := block.(*wrapper); ok || boring.Enabled { ++ if _, ok := block.(*wrapper); ok { + t.Skip("NewGCMWithRandomNonce requires an AES block cipher") + } + a, _ := cipher.NewGCMWithRandomNonce(block) +diff --git a/src/crypto/des/cipher.go b/src/crypto/des/cipher.go +index 81ba766b2527d2..a0fc16647c5b5f 100644 +--- a/src/crypto/des/cipher.go ++++ b/src/crypto/des/cipher.go +@@ -6,6 +6,7 @@ package des + + import ( + "crypto/cipher" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/alias" + "crypto/internal/fips140only" + "errors" +@@ -36,6 +37,9 @@ func NewCipher(key []byte) (cipher.Block, error) { + if len(key) != 8 { + return nil, KeySizeError(len(key)) + } ++ if boring.Enabled && boring.SupportsDESCipher() { ++ return boring.NewDESCipher(key) ++ } + + c := new(desCipher) + c.generateSubkeys(key) +@@ -84,6 +88,9 @@ func NewTripleDESCipher(key []byte) (cipher.Block, error) { + if len(key) != 24 { + return nil, KeySizeError(len(key)) + } ++ if boring.Enabled && boring.SupportsTripleDESCipher() { ++ return boring.NewTripleDESCipher(key) ++ } + + c := new(tripleDESCipher) + c.cipher1.generateSubkeys(key[:8]) +diff --git a/src/crypto/dsa/boring.go b/src/crypto/dsa/boring.go +new file mode 100644 +index 00000000000000..7ea0c8c423e9ee +--- /dev/null ++++ b/src/crypto/dsa/boring.go +@@ -0,0 +1,113 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package dsa ++ ++import ( ++ boring "crypto/internal/backend" ++ "crypto/internal/backend/bbig" ++ "crypto/internal/boring/bcache" ++ "math/big" ++) ++ ++// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. ++// ++// The first operation on a PublicKey or PrivateKey makes a parallel ++// BoringCrypto key and saves it in pubCache or privCache. ++// ++// We could just assume that once used in a sign/verify/encrypt/decrypt operation, ++// a particular key is never again modified, but that has not been a ++// stated assumption before. Just in case there is any existing code that ++// does modify the key between operations, we save the original values ++// alongside the cached BoringCrypto key and check that the real key ++// still matches before using the cached key. The theory is that the real ++// operations are significantly more expensive than the comparison. ++ ++type boringPub struct { ++ key *boring.PublicKeyDSA ++ orig PublicKey ++} ++ ++var pubCache bcache.Cache[PublicKey, boringPub] ++var privCache bcache.Cache[PrivateKey, boringPriv] ++ ++func init() { ++ pubCache.Register() ++ privCache.Register() ++} ++ ++func boringPublicKey(pub *PublicKey) (*boring.PublicKeyDSA, error) { ++ b := pubCache.Get(pub) ++ if b != nil && publicKeyEqual(&b.orig, pub) { ++ return b.key, nil ++ } ++ ++ b = new(boringPub) ++ b.orig = copyPublicKey(pub) ++ key, err := boring.NewPublicKeyDSA(bbig.Enc(b.orig.P), bbig.Enc(b.orig.Q), bbig.Enc(b.orig.G), bbig.Enc(b.orig.Y)) ++ if err != nil { ++ return nil, err ++ } ++ b.key = key ++ pubCache.Put(pub, b) ++ return key, nil ++} ++ ++type boringPriv struct { ++ key *boring.PrivateKeyDSA ++ orig PrivateKey ++} ++ ++func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyDSA, error) { ++ b := privCache.Get(priv) ++ if b != nil && privateKeyEqual(&b.orig, priv) { ++ return b.key, nil ++ } ++ ++ b = new(boringPriv) ++ b.orig = copyPrivateKey(priv) ++ ++ P := b.orig.P ++ Q := b.orig.Q ++ G := b.orig.G ++ X := b.orig.X ++ Y := b.orig.Y ++ ++ key, err := boring.NewPrivateKeyDSA(bbig.Enc(P), bbig.Enc(Q), bbig.Enc(G), bbig.Enc(X), bbig.Enc(Y)) ++ if err != nil { ++ return nil, err ++ } ++ b.key = key ++ privCache.Put(priv, b) ++ return key, nil ++} ++ ++func publicKeyEqual(k1, k2 *PublicKey) bool { ++ return k1.Y != nil && k1.Y.Cmp(k2.Y) == 0 && k1.P.Cmp(k2.P) == 0 && k1.Q.Cmp(k2.Q) == 0 && k1.G.Cmp(k2.G) == 0 ++} ++ ++func copyPublicKey(k *PublicKey) PublicKey { ++ return PublicKey{ ++ Parameters: Parameters{ ++ P: new(big.Int).Set(k.P), ++ Q: new(big.Int).Set(k.Q), ++ G: new(big.Int).Set(k.G), ++ }, ++ Y: new(big.Int).Set(k.Y), ++ } ++} ++ ++func privateKeyEqual(k1, k2 *PrivateKey) bool { ++ return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) && ++ k1.X.Cmp(k2.X) == 0 ++} ++ ++func copyPrivateKey(k *PrivateKey) PrivateKey { ++ return PrivateKey{ ++ PublicKey: copyPublicKey(&k.PublicKey), ++ X: new(big.Int).Set(k.X), ++ } ++} +diff --git a/src/crypto/dsa/dsa.go b/src/crypto/dsa/dsa.go +index 6724f861b7f2f0..4bda65558882a2 100644 +--- a/src/crypto/dsa/dsa.go ++++ b/src/crypto/dsa/dsa.go +@@ -18,8 +18,13 @@ import ( + "io" + "math/big" + ++ boring "crypto/internal/backend" ++ "crypto/internal/backend/bbig" + "crypto/internal/fips140only" + "crypto/internal/rand" ++ ++ "golang.org/x/crypto/cryptobyte" ++ "golang.org/x/crypto/cryptobyte/asn1" + ) + + // Parameters represents the domain parameters for a key. These parameters can +@@ -91,6 +96,17 @@ func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes + return errors.New("crypto/dsa: invalid ParameterSizes") + } + ++ if boring.Enabled && boring.SupportsDSA(L, N) { ++ p, q, g, err := boring.GenerateParametersDSA(L, N) ++ if err != nil { ++ return err ++ } ++ params.P = bbig.Dec(p) ++ params.Q = bbig.Dec(q) ++ params.G = bbig.Dec(g) ++ return nil ++ } ++ + qBytes := make([]byte, N/8) + pBytes := make([]byte, L/8) + +@@ -170,6 +186,17 @@ func GenerateKey(priv *PrivateKey, rand io.Reader) error { + return errors.New("crypto/dsa: parameters not set up before generating key") + } + ++ if boring.Enabled && boring.SupportsDSA(priv.P.BitLen(), priv.Q.BitLen()) { ++ x, y, err := boring.GenerateKeyDSA(bbig.Enc(priv.P), bbig.Enc(priv.Q), bbig.Enc(priv.G)) ++ if err != nil { ++ return err ++ } ++ priv.X = bbig.Dec(x) ++ priv.Y = bbig.Dec(y) ++ ++ return nil ++ } ++ + x := new(big.Int) + xBytes := make([]byte, priv.Q.BitLen()/8) + +@@ -229,6 +256,18 @@ func Sign(random io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err e + err = ErrInvalidPublicKey + return + } ++ ++ if boring.Enabled && boring.SupportsDSA(priv.P.BitLen(), priv.Q.BitLen()) { ++ b, err := boringPrivateKey(priv) ++ if err != nil { ++ return nil, nil, err ++ } ++ ++ r, s, err := boring.SignDSA(b, hash, parseSignature) ++ ++ return bbig.Dec(r), bbig.Dec(s), err ++ } ++ + n >>= 3 + + var attempts int +@@ -292,6 +331,14 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { + panic("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode") + } + ++ if boring.Enabled && boring.SupportsDSA(pub.P.BitLen(), pub.Q.BitLen()) { ++ bkey, err := boringPublicKey(pub) ++ if err != nil { ++ return false ++ } ++ ++ return boring.VerifyDSA(bkey, hash, bbig.Enc(r), bbig.Enc(s), encodeSignature) ++ } + // FIPS 186-3, section 4.7 + + if pub.P.Sign() == 0 { +@@ -328,3 +375,44 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { + + return v.Cmp(r) == 0 + } ++ ++func parseSignature(sig []byte) (boring.BigInt, boring.BigInt, error) { ++ var r, s []byte ++ var inner cryptobyte.String ++ input := cryptobyte.String(sig) ++ if !input.ReadASN1(&inner, asn1.SEQUENCE) || ++ !input.Empty() || ++ !inner.ReadASN1Integer(&r) || ++ !inner.ReadASN1Integer(&s) || ++ !inner.Empty() { ++ return nil, nil, errors.New("invalid ASN.1") ++ } ++ return bbig.Enc(new(big.Int).SetBytes(r)), bbig.Enc(new(big.Int).SetBytes(s)), nil ++} ++ ++func encodeSignature(r, s boring.BigInt) ([]byte, error) { ++ var b cryptobyte.Builder ++ b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { ++ addASN1IntBytes(b, bbig.Dec(r).Bytes()) ++ addASN1IntBytes(b, bbig.Dec(s).Bytes()) ++ }) ++ return b.Bytes() ++} ++ ++// addASN1IntBytes encodes in ASN.1 a positive integer represented as ++// a big-endian byte slice with zero or more leading zeroes. ++func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) { ++ for len(bytes) > 0 && bytes[0] == 0 { ++ bytes = bytes[1:] ++ } ++ if len(bytes) == 0 { ++ b.SetError(errors.New("invalid integer")) ++ return ++ } ++ b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) { ++ if bytes[0]&0x80 != 0 { ++ c.AddUint8(0) ++ } ++ c.AddBytes(bytes) ++ }) ++} +diff --git a/src/crypto/dsa/dsa_test.go b/src/crypto/dsa/dsa_test.go +index ad85eac0a7f0b1..bf0d5eb077b2b1 100644 +--- a/src/crypto/dsa/dsa_test.go ++++ b/src/crypto/dsa/dsa_test.go +@@ -5,8 +5,11 @@ + package dsa + + import ( ++ "crypto/fips140" ++ boring "crypto/internal/backend" + "crypto/rand" + "math/big" ++ "runtime" + "testing" + ) + +@@ -83,6 +86,11 @@ func fromHex(s string) *big.Int { + } + + func TestSignAndVerify(t *testing.T) { ++ if boring.Enabled && runtime.GOOS == "linux" && fips140.Enabled() { ++ // Some OpenSSL providers don't support DSA signing in FIPS mode. ++ t.Skip("skipping DSA test in FIPS mode") ++ } ++ + priv := PrivateKey{ + PublicKey: PublicKey{ + Parameters: Parameters{ +diff --git a/src/crypto/dsa/notboring.go b/src/crypto/dsa/notboring.go +new file mode 100644 +index 00000000000000..cd02ff5a00c3dc +--- /dev/null ++++ b/src/crypto/dsa/notboring.go +@@ -0,0 +1,16 @@ ++// Copyright 2022 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !goexperiment.systemcrypto ++ ++package dsa ++ ++import boring "crypto/internal/backend" ++ ++func boringPublicKey(*PublicKey) (*boring.PublicKeyDSA, error) { ++ panic("boringcrypto: not available") ++} ++func boringPrivateKey(*PrivateKey) (*boring.PrivateKeyDSA, error) { ++ panic("boringcrypto: not available") ++} +diff --git a/src/crypto/ecdh/ecdh.go b/src/crypto/ecdh/ecdh.go +index 3f85a2833697a9..a5717e22db8d78 100644 +--- a/src/crypto/ecdh/ecdh.go ++++ b/src/crypto/ecdh/ecdh.go +@@ -8,7 +8,7 @@ package ecdh + + import ( + "crypto" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/ecdh" + "crypto/subtle" + "errors" +diff --git a/src/crypto/ecdh/ecdh_test.go b/src/crypto/ecdh/ecdh_test.go +index cfa919ade97ad0..40e316387173dc 100644 +--- a/src/crypto/ecdh/ecdh_test.go ++++ b/src/crypto/ecdh/ecdh_test.go +@@ -9,6 +9,7 @@ import ( + "crypto" + "crypto/cipher" + "crypto/ecdh" ++ boring "crypto/internal/backend" + "crypto/rand" + "crypto/sha256" + "encoding/hex" +@@ -207,13 +208,17 @@ func TestX25519Failure(t *testing.T) { + } + + func testX25519Failure(t *testing.T, private, public []byte) { ++ if boring.Enabled && !boring.SupportsCurve("X25519") { ++ t.Skip("backend does not support X25519") ++ } + priv, err := ecdh.X25519().NewPrivateKey(private) + if err != nil { + t.Fatal(err) + } + pub, err := ecdh.X25519().NewPublicKey(public) + if err != nil { +- t.Fatal(err) ++ // Some backends may reject the public key at import time, that's fine. ++ return + } + secret, err := priv.ECDH(pub) + if err == nil { +@@ -279,6 +284,9 @@ var invalidPrivateKeys = map[ecdh.Curve][]string{ + } + + func TestNewPrivateKey(t *testing.T) { ++ if boring.Enabled { ++ t.Skip("skipping test with different results when using different backends") ++ } + testAllCurves(t, func(t *testing.T, curve ecdh.Curve) { + for _, input := range invalidPrivateKeys[curve] { + k, err := curve.NewPrivateKey(hexDecode(t, input)) +@@ -364,7 +372,12 @@ func testAllCurves(t *testing.T, f func(t *testing.T, curve ecdh.Curve)) { + t.Run("P256", func(t *testing.T) { f(t, ecdh.P256()) }) + t.Run("P384", func(t *testing.T) { f(t, ecdh.P384()) }) + t.Run("P521", func(t *testing.T) { f(t, ecdh.P521()) }) +- t.Run("X25519", func(t *testing.T) { f(t, ecdh.X25519()) }) ++ t.Run("X25519", func(t *testing.T) { ++ if boring.Enabled && !boring.SupportsCurve("X25519") { ++ t.Skip("backend does not support X25519") ++ } ++ f(t, ecdh.X25519()) ++ }) + } + + func BenchmarkECDH(b *testing.B) { +diff --git a/src/crypto/ecdh/nist.go b/src/crypto/ecdh/nist.go +index 13865ea7012989..fb46bf4822fb6a 100644 +--- a/src/crypto/ecdh/nist.go ++++ b/src/crypto/ecdh/nist.go +@@ -6,7 +6,7 @@ package ecdh + + import ( + "bytes" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/ecdh" + "crypto/internal/fips140only" + "crypto/internal/rand" +@@ -123,24 +123,26 @@ func (c *nistCurve) NewPublicKey(key []byte) (*PublicKey, error) { + if len(key) == 0 || key[0] != 4 { + return nil, errors.New("crypto/ecdh: invalid public key") + } +- k := &PublicKey{ +- curve: c, +- publicKey: bytes.Clone(key), +- } + if boring.Enabled { +- bk, err := boring.NewPublicKeyECDH(c.name, k.publicKey) ++ bk, err := boring.NewPublicKeyECDH(c.name, key) + if err != nil { + return nil, errors.New("crypto/ecdh: invalid public key") + } +- k.boring = bk +- } else { +- fk, err := c.newPublicKey(key) +- if err != nil { +- return nil, err +- } +- k.fips = fk ++ return &PublicKey{ ++ curve: c, ++ publicKey: bk.Bytes(), ++ boring: bk, ++ }, nil + } +- return k, nil ++ fk, err := c.newPublicKey(key) ++ if err != nil { ++ return nil, err ++ } ++ return &PublicKey{ ++ curve: c, ++ publicKey: bytes.Clone(key), ++ fips: fk, ++ }, nil + } + + func (c *nistCurve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) { +diff --git a/src/crypto/ecdh/x25519.go b/src/crypto/ecdh/x25519.go +index 21a921aa12d44d..be8b1441c62844 100644 +--- a/src/crypto/ecdh/x25519.go ++++ b/src/crypto/ecdh/x25519.go +@@ -6,6 +6,7 @@ package ecdh + + import ( + "bytes" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/edwards25519/field" + "crypto/internal/fips140only" + "crypto/internal/rand" +@@ -38,6 +39,23 @@ func (c *x25519Curve) GenerateKey(r io.Reader) (*PrivateKey, error) { + if fips140only.Enforced() { + return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode") + } ++ if boring.Enabled && boring.SupportsCurve("X25519") && rand.IsDefaultReader(r) { ++ key, bytes, err := boring.GenerateKeyECDH(c.String()) ++ if err != nil { ++ return nil, err ++ } ++ pub, err := key.PublicKey() ++ if err != nil { ++ return nil, err ++ } ++ k := &PrivateKey{ ++ curve: c, ++ privateKey: bytes, ++ publicKey: &PublicKey{curve: c, publicKey: pub.Bytes(), boring: pub}, ++ boring: key, ++ } ++ return k, nil ++ } + r = rand.CustomReader(r) + key := make([]byte, x25519PrivateKeySize) + if _, err := io.ReadFull(r, key); err != nil { +@@ -53,6 +71,23 @@ func (c *x25519Curve) NewPrivateKey(key []byte) (*PrivateKey, error) { + if len(key) != x25519PrivateKeySize { + return nil, errors.New("crypto/ecdh: invalid private key size") + } ++ if boring.Enabled && boring.SupportsCurve("X25519") { ++ bk, err := boring.NewPrivateKeyECDH(c.String(), key) ++ if err != nil { ++ return nil, errors.New("crypto/ecdh: invalid private key") ++ } ++ pub, err := bk.PublicKey() ++ if err != nil { ++ return nil, errors.New("crypto/ecdh: invalid private key") ++ } ++ k := &PrivateKey{ ++ curve: c, ++ privateKey: bytes.Clone(key), ++ publicKey: &PublicKey{curve: c, publicKey: pub.Bytes(), boring: pub}, ++ boring: bk, ++ } ++ return k, nil ++ } + publicKey := make([]byte, x25519PublicKeySize) + x25519Basepoint := [32]byte{9} + x25519ScalarMult(publicKey, key, x25519Basepoint[:]) +@@ -73,6 +108,17 @@ func (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) { + if len(key) != x25519PublicKeySize { + return nil, errors.New("crypto/ecdh: invalid public key") + } ++ if boring.Enabled && boring.SupportsCurve("X25519") { ++ bk, err := boring.NewPublicKeyECDH(c.String(), key) ++ if err != nil { ++ return nil, errors.New("crypto/ecdh: invalid public key") ++ } ++ return &PublicKey{ ++ curve: c, ++ publicKey: bk.Bytes(), ++ boring: bk, ++ }, nil ++ } + return &PublicKey{ + curve: c, + publicKey: bytes.Clone(key), +@@ -80,6 +126,9 @@ func (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) { + } + + func (c *x25519Curve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) { ++ if boring.Enabled && boring.SupportsCurve("X25519") { ++ return boring.ECDH(local.boring, remote.boring) ++ } + out := make([]byte, x25519SharedSecretSize) + x25519ScalarMult(out, local.privateKey, remote.publicKey) + if isZero(out) { +diff --git a/src/crypto/ecdsa/badlinkname.go b/src/crypto/ecdsa/badlinkname.go +new file mode 100644 +index 00000000000000..168efdb820c85b +--- /dev/null ++++ b/src/crypto/ecdsa/badlinkname.go +@@ -0,0 +1,19 @@ ++// Copyright 2025 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package ecdsa ++ ++import _ "unsafe" ++ ++// The symbols below are pulled by some crypto backend in the Microsoft build of Go compiler. ++// We provide a push linkname here, to keep them accessible with pull linknames. ++ ++//go:linkname encodeSignature ++//go:linkname parseSignature ++ ++// This supplements other linknames that are already added by ++// https://github.com/golang/go/commit/41aab30bd260297ad8ddad47e98fdf8390a9a67e ++// See that commit for more information. +diff --git a/src/crypto/ecdsa/boring.go b/src/crypto/ecdsa/boring.go +index 275c60b4de49eb..ff8bddf28c4545 100644 +--- a/src/crypto/ecdsa/boring.go ++++ b/src/crypto/ecdsa/boring.go +@@ -2,13 +2,13 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + package ecdsa + + import ( +- "crypto/internal/boring" +- "crypto/internal/boring/bbig" ++ boring "crypto/internal/backend" ++ "crypto/internal/backend/bbig" + "crypto/internal/boring/bcache" + "math/big" + ) +diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go +index 40a89017570171..a0fb823b7c9197 100644 +--- a/src/crypto/ecdsa/ecdsa.go ++++ b/src/crypto/ecdsa/ecdsa.go +@@ -20,8 +20,8 @@ import ( + "crypto" + "crypto/ecdh" + "crypto/elliptic" +- "crypto/internal/boring" +- "crypto/internal/boring/bbig" ++ boring "crypto/internal/backend" ++ "crypto/internal/backend/bbig" + "crypto/internal/fips140/ecdsa" + "crypto/internal/fips140/nistec" + "crypto/internal/fips140cache" +@@ -339,7 +339,7 @@ func (priv *PrivateKey) Sign(random io.Reader, digest []byte, opts crypto.Signer + // ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed + // in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom]. + func GenerateKey(c elliptic.Curve, r io.Reader) (*PrivateKey, error) { +- if boring.Enabled && rand.IsDefaultReader(r) { ++ if boring.Enabled && rand.IsDefaultReader(r) && boring.SupportsCurve(c.Params().Name) { + x, y, d, err := boring.GenerateKeyECDSA(c.Params().Name) + if err != nil { + return nil, err +@@ -389,7 +389,7 @@ func SignASN1(r io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) { + return nil, errors.New("ecdsa: hash cannot be empty") + } + +- if boring.Enabled && rand.IsDefaultReader(r) { ++ if boring.Enabled && rand.IsDefaultReader(r) && boring.SupportsCurve(priv.Curve.Params().Name) { + b, err := boringPrivateKey(priv) + if err != nil { + return nil, err +@@ -510,7 +510,7 @@ func VerifyASN1(pub *PublicKey, hash, sig []byte) bool { + return false + } + +- if boring.Enabled { ++ if boring.Enabled && boring.SupportsCurve(pub.Curve.Params().Name) { + key, err := boringPublicKey(pub) + if err != nil { + return false +diff --git a/src/crypto/ecdsa/notboring.go b/src/crypto/ecdsa/notboring.go +index 039bd82ed21f9f..69a97d9bf250be 100644 +--- a/src/crypto/ecdsa/notboring.go ++++ b/src/crypto/ecdsa/notboring.go +@@ -2,11 +2,11 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !boringcrypto ++//go:build !goexperiment.systemcrypto + + package ecdsa + +-import "crypto/internal/boring" ++import boring "crypto/internal/backend" + + func boringPublicKey(*PublicKey) (*boring.PublicKeyECDSA, error) { + panic("boringcrypto: not available") +diff --git a/src/crypto/ed25519/boring.go b/src/crypto/ed25519/boring.go +new file mode 100644 +index 00000000000000..4e18cdfbaad4fd +--- /dev/null ++++ b/src/crypto/ed25519/boring.go +@@ -0,0 +1,71 @@ ++// Copyright 2023 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package ed25519 ++ ++import ( ++ boring "crypto/internal/backend" ++ "crypto/internal/boring/bcache" ++ "unsafe" ++) ++ ++var pubCache bcache.Cache[byte, boringPub] ++var privCache bcache.Cache[byte, boringPriv] ++ ++func init() { ++ if boring.Enabled && boring.SupportsEd25519() { ++ pubCache.Register() ++ privCache.Register() ++ } ++} ++ ++type boringPub struct { ++ key boring.PublicKeyEd25519 ++ orig [PublicKeySize]byte ++} ++ ++func boringPublicKey(pub PublicKey) (boring.PublicKeyEd25519, error) { ++ // Use the pointer to the underlying pub array as key. ++ p := unsafe.SliceData(pub) ++ b := pubCache.Get(p) ++ if b != nil && PublicKey(b.orig[:]).Equal(pub) { ++ return b.key, nil ++ } ++ ++ b = new(boringPub) ++ copy(b.orig[:], pub) ++ key, err := boring.NewPublicKeyEd25519(b.orig[:]) ++ if err != nil { ++ return key, err ++ } ++ b.key = key ++ pubCache.Put(p, b) ++ return key, nil ++} ++ ++type boringPriv struct { ++ key boring.PrivateKeyEd25519 ++ orig [PrivateKeySize]byte ++} ++ ++func boringPrivateKey(priv PrivateKey) (boring.PrivateKeyEd25519, error) { ++ // Use the pointer to the underlying priv array as key. ++ p := unsafe.SliceData(priv) ++ b := privCache.Get(p) ++ if b != nil && PrivateKey(b.orig[:]).Equal(priv) { ++ return b.key, nil ++ } ++ ++ b = new(boringPriv) ++ copy(b.orig[:], priv) ++ key, err := boring.NewPrivateKeyEd25519(b.orig[:]) ++ if err != nil { ++ return key, err ++ } ++ b.key = key ++ privCache.Put(p, b) ++ return key, nil ++} +diff --git a/src/crypto/ed25519/ed25519.go b/src/crypto/ed25519/ed25519.go +index ed599ad2908dfb..b3106b9ed83811 100644 +--- a/src/crypto/ed25519/ed25519.go ++++ b/src/crypto/ed25519/ed25519.go +@@ -17,6 +17,7 @@ package ed25519 + + import ( + "crypto" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/ed25519" + "crypto/internal/fips140cache" + "crypto/internal/fips140only" +@@ -27,6 +28,7 @@ import ( + "internal/godebug" + "io" + "strconv" ++ "sync" + ) + + const ( +@@ -159,6 +161,19 @@ func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error) { + random = rand.Reader + } + } ++ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsEd25519() { ++ priv, err := boring.GenerateKeyEd25519() ++ if err != nil { ++ return nil, nil, err ++ } ++ privData, err := priv.Bytes() ++ if err != nil { ++ return nil, nil, err ++ } ++ privKey := PrivateKey(privData) ++ pubKey := privKey.Public().(PublicKey) ++ return pubKey, privKey, err ++ } + + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(random) { + return nil, nil, errors.New("crypto/ed25519: only crypto/rand.Reader is allowed in FIPS 140-only mode") +@@ -188,6 +203,17 @@ func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error) { + // with RFC 8032. RFC 8032's private keys correspond to seeds in this + // package. + func NewKeyFromSeed(seed []byte) PrivateKey { ++ if boring.Enabled && boring.SupportsEd25519() { ++ key, err := boring.NewPrivateKeyEd25519FromSeed(seed) ++ if err != nil { ++ panic(err) ++ } ++ d, err := key.Bytes() ++ if err != nil { ++ panic(err) ++ } ++ return PrivateKey(d) ++ } + // Outline the function body so that the returned key can be stack-allocated. + privateKey := make([]byte, PrivateKeySize) + newKeyFromSeed(privateKey, seed) +@@ -206,6 +232,17 @@ func newKeyFromSeed(privateKey, seed []byte) { + // Sign signs the message with privateKey and returns a signature. It will + // panic if len(privateKey) is not [PrivateKeySize]. + func Sign(privateKey PrivateKey, message []byte) []byte { ++ if boring.Enabled && boring.SupportsEd25519() { ++ priv, err := boringPrivateKey(privateKey) ++ if err != nil { ++ panic(err) ++ } ++ signature, err := boring.SignEd25519(priv, message) ++ if err != nil { ++ panic(err) ++ } ++ return signature ++ } + // Outline the function body so that the returned signature can be + // stack-allocated. + signature := make([]byte, SignatureSize) +@@ -235,6 +272,32 @@ func Verify(publicKey PublicKey, message, sig []byte) bool { + return VerifyWithOptions(publicKey, message, sig, &Options{Hash: crypto.Hash(0)}) == nil + } + ++// testMalleability returns true if the crypto backend correctly rejects ++// malleable signatures. The only known backend that fails to do so is ++// OpenSSL version 1.1.1a or lower. ++// See https://github.com/openssl/openssl/issues/7693. ++var testMalleability = sync.OnceValue(func() bool { ++ msg := []byte{0x54, 0x65, 0x73, 0x74} ++ sig := []byte{ ++ 0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a, ++ 0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b, ++ 0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67, ++ 0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d, ++ 0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33, ++ 0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d, ++ } ++ pkey := []byte{ ++ 0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5, ++ 0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34, ++ 0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa, ++ } ++ pub, err := boring.NewPublicKeyEd25519(pkey) ++ if err != nil { ++ return false ++ } ++ return boring.VerifyEd25519(pub, msg, sig) != nil ++}) ++ + // VerifyWithOptions reports whether sig is a valid signature of message by + // publicKey. A valid signature is indicated by returning a nil error. It will + // panic if len(publicKey) is not [PublicKeySize]. +@@ -263,6 +326,13 @@ func VerifyWithOptions(publicKey PublicKey, message, sig []byte, opts *Options) + } + return ed25519.VerifyCtx(k, message, sig, opts.Context) + case opts.Hash == crypto.Hash(0): // Ed25519 ++ if boring.Enabled && boring.SupportsEd25519() && testMalleability() { ++ pub, err := boringPublicKey(publicKey) ++ if err != nil { ++ return err ++ } ++ return boring.VerifyEd25519(pub, message, sig) ++ } + return ed25519.Verify(k, message, sig) + default: + return errors.New("ed25519: expected opts.Hash zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)") +diff --git a/src/crypto/ed25519/ed25519_test.go b/src/crypto/ed25519/ed25519_test.go +index c8a23e3246a949..843dbee858a5de 100644 +--- a/src/crypto/ed25519/ed25519_test.go ++++ b/src/crypto/ed25519/ed25519_test.go +@@ -9,12 +9,15 @@ import ( + "bytes" + "compress/gzip" + "crypto" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "crypto/rand" + "crypto/sha512" + "encoding/hex" ++ "internal/systemcrypto" + "log" + "os" ++ "runtime" + "strings" + "testing" + ) +@@ -316,7 +319,7 @@ func TestGolden(t *testing.T) { + copy(priv[32:], pubKey) + + sig2 := Sign(priv[:], msg) +- if !bytes.Equal(sig, sig2[:]) { ++ if !bytes.Equal(sig, sig2[:]) && !(systemcrypto.Enabled() && runtime.GOOS == "darwin") { + t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2) + } + +@@ -371,6 +374,11 @@ func TestAllocations(t *testing.T) { + cryptotest.SkipTestAllocations(t) + seed := make([]byte, SeedSize) + priv := NewKeyFromSeed(seed) ++ expectedAllocations := 0.0 ++ if boring.Enabled { ++ expectedAllocations = 6 ++ } ++ + if allocs := testing.AllocsPerRun(100, func() { + message := []byte("Hello, world!") + pub := priv.Public().(PublicKey) +@@ -378,8 +386,8 @@ func TestAllocations(t *testing.T) { + if !Verify(pub, message, signature) { + t.Fatal("signature didn't verify") + } +- }); allocs > 0 { +- t.Errorf("expected zero allocations, got %0.1f", allocs) ++ }); allocs > expectedAllocations { ++ t.Errorf("expected %v allocations, got %0.1f", expectedAllocations, allocs) + } + } + +diff --git a/src/crypto/ed25519/notboring.go b/src/crypto/ed25519/notboring.go +new file mode 100644 +index 00000000000000..77b69a3be88183 +--- /dev/null ++++ b/src/crypto/ed25519/notboring.go +@@ -0,0 +1,16 @@ ++// Copyright 2023 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !goexperiment.systemcrypto ++ ++package ed25519 ++ ++import boring "crypto/internal/backend" ++ ++func boringPublicKey(PublicKey) (boring.PublicKeyEd25519, error) { ++ panic("boringcrypto: not available") ++} ++func boringPrivateKey(PrivateKey) (boring.PrivateKeyEd25519, error) { ++ panic("boringcrypto: not available") ++} +diff --git a/src/crypto/fips140/enforcement_test.go b/src/crypto/fips140/enforcement_test.go +index d230c0f4ec1fa1..56020ea5f6c2a3 100644 +--- a/src/crypto/fips140/enforcement_test.go ++++ b/src/crypto/fips140/enforcement_test.go +@@ -7,6 +7,7 @@ package fips140_test + import ( + "crypto/des" + "crypto/fips140" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "testing" + ) +@@ -25,6 +26,9 @@ func isAllowed() bool { + } + + func TestWithoutEnforcement(t *testing.T) { ++ if boring.Enabled { ++ t.Skip("GODEBUG=fips140=only not supported") ++ } + if !fips140.Enforced() { + cryptotest.RerunWithFIPS140Enforced(t) + return +diff --git a/src/crypto/fips140/fips140.go b/src/crypto/fips140/fips140.go +index d3f63d3bf18fcb..7197af537228aa 100644 +--- a/src/crypto/fips140/fips140.go ++++ b/src/crypto/fips140/fips140.go +@@ -11,6 +11,7 @@ + package fips140 + + import ( ++ bfips140 "crypto/internal/backend/fips140" + "crypto/internal/fips140" + "crypto/internal/fips140/check" + ) +@@ -27,7 +28,7 @@ func Enabled() bool { + if fips140.Enabled && !check.Verified { + panic("crypto/fips140: FIPS 140-3 mode enabled, but integrity check didn't pass") + } +- return fips140.Enabled ++ return fips140.Enabled || bfips140.Enabled() + } + + // Version returns the FIPS 140-3 Go Cryptographic Module version (such as +diff --git a/src/crypto/hkdf/hkdf.go b/src/crypto/hkdf/hkdf.go +index 88439922a5032e..5a3ae17e80efbc 100644 +--- a/src/crypto/hkdf/hkdf.go ++++ b/src/crypto/hkdf/hkdf.go +@@ -11,6 +11,7 @@ + package hkdf + + import ( ++ boring "crypto/internal/backend" + "crypto/internal/fips140/hkdf" + "crypto/internal/fips140hash" + "crypto/internal/fips140only" +@@ -29,6 +30,9 @@ func Extract[H hash.Hash](h func() H, secret, salt []byte) ([]byte, error) { + if err := checkFIPS140Only(fh, secret); err != nil { + return nil, err + } ++ if boring.Enabled && boring.SupportsHKDF() { ++ return boring.ExtractHKDF(fh, secret, salt) ++ } + return hkdf.Extract(fh, secret, salt), nil + } + +@@ -50,6 +54,9 @@ func Expand[H hash.Hash](h func() H, pseudorandomKey []byte, info string, keyLen + return nil, errors.New("hkdf: requested key length too large") + } + ++ if boring.Enabled && boring.SupportsHKDF() { ++ return boring.ExpandHKDF(fh, pseudorandomKey, []byte(info), keyLength) ++ } + return hkdf.Expand(fh, pseudorandomKey, info, keyLength), nil + } + +@@ -67,6 +74,13 @@ func Key[Hash hash.Hash](h func() Hash, secret, salt []byte, info string, keyLen + return nil, errors.New("hkdf: requested key length too large") + } + ++ if boring.Enabled && boring.SupportsHKDF() { ++ pseudorandomKey, err := boring.ExtractHKDF(fh, secret, salt) ++ if err != nil { ++ return nil, err ++ } ++ return boring.ExpandHKDF(fh, pseudorandomKey, []byte(info), keyLength) ++ } + return hkdf.Key(fh, secret, salt, info, keyLength), nil + } + +diff --git a/src/crypto/hkdf/hkdf_test.go b/src/crypto/hkdf/hkdf_test.go +index 57d90f88e93e75..4069ab057a2525 100644 +--- a/src/crypto/hkdf/hkdf_test.go ++++ b/src/crypto/hkdf/hkdf_test.go +@@ -6,7 +6,7 @@ package hkdf + + import ( + "bytes" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140" + "crypto/md5" + "crypto/sha1" +diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go +index e7976e25193dfe..58b357ee52c328 100644 +--- a/src/crypto/hmac/hmac.go ++++ b/src/crypto/hmac/hmac.go +@@ -22,7 +22,7 @@ timing side-channels: + package hmac + + import ( +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/hmac" + "crypto/internal/fips140hash" + "crypto/internal/fips140only" +@@ -37,13 +37,6 @@ import ( + // the returned Hash does not implement [encoding.BinaryMarshaler] + // or [encoding.BinaryUnmarshaler]. + func New(h func() hash.Hash, key []byte) hash.Hash { +- if boring.Enabled { +- hm := boring.NewHMAC(h, key) +- if hm != nil { +- return hm +- } +- // BoringCrypto did not recognize h, so fall through to standard Go code. +- } + h = fips140hash.UnwrapNew(h) + if fips140only.Enforced() { + if len(key) < 112/8 { +@@ -53,6 +46,13 @@ func New(h func() hash.Hash, key []byte) hash.Hash { + panic("crypto/hmac: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + } ++ if boring.Enabled { ++ hm := boring.NewHMAC(h, key) ++ if hm != nil { ++ return hm ++ } ++ // BoringCrypto did not recognize h, so fall through to standard Go code. ++ } + return hmac.New(h, key) + } + +diff --git a/src/crypto/hmac/hmac_test.go b/src/crypto/hmac/hmac_test.go +index b324d1f02cda7d..1df17bdc90a1f0 100644 +--- a/src/crypto/hmac/hmac_test.go ++++ b/src/crypto/hmac/hmac_test.go +@@ -5,7 +5,7 @@ + package hmac + + import ( +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "crypto/internal/fips140hash" + "crypto/md5" +diff --git a/src/crypto/hpke/aead.go b/src/crypto/hpke/aead.go +index fb55c97ddf20c9..fb036863cde2dd 100644 +--- a/src/crypto/hpke/aead.go ++++ b/src/crypto/hpke/aead.go +@@ -6,6 +6,7 @@ package hpke + + import ( + "crypto/cipher" ++ boring "crypto/internal/backend" + "errors" + "fmt" + +@@ -77,10 +78,15 @@ var aes256GCM = &aead{ + } + + var chacha20poly1305AEAD = &aead{ +- nK: chacha20poly1305.KeySize, +- nN: chacha20poly1305.NonceSize, +- new: chacha20poly1305.New, +- id: 0x0003, ++ nK: chacha20poly1305.KeySize, ++ nN: chacha20poly1305.NonceSize, ++ new: func(key []byte) (cipher.AEAD, error) { ++ if boring.Enabled && boring.SupportsChaCha20Poly1305() { ++ return boring.NewChaCha20Poly1305(key) ++ } ++ return chacha20poly1305.New(key) ++ }, ++ id: 0x0003, + } + + func (a *aead) ID() uint16 { +diff --git a/src/crypto/internal/backend/backend_darwin.go b/src/crypto/internal/backend/backend_darwin.go +new file mode 100644 +index 00000000000000..f3d79d6b318d8a +--- /dev/null ++++ b/src/crypto/internal/backend/backend_darwin.go +@@ -0,0 +1,441 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++// Package darwin provides access to DarwinCrypto implementation functions. ++// Check the variable Enabled to find out whether DarwinCrypto is available. ++// If DarwinCrypto is not available, the functions in this package all panic. ++package backend ++ ++import ( ++ "crypto" ++ "crypto/cipher" ++ "crypto/internal/boring/sig" ++ "crypto/internal/fips140only" ++ "errors" ++ "hash" ++ "io" ++ _ "unsafe" ++ ++ "github.com/microsoft/go-crypto-darwin/xcrypto" ++) ++ ++func init() { ++ // Darwin is considered FIPS compliant. ++ if err := checkFIPS(func() bool { return true }); err != nil { ++ panic("darwincrypto: " + err.Error()) ++ } ++ sig.BoringCrypto() ++ fips140only.BackendApprovedHash = FIPSApprovedHash ++} ++ ++// Enabled controls whether FIPS crypto is enabled. ++const Enabled = true ++ ++type BigInt = xcrypto.BigInt ++ ++const RandReader = xcrypto.RandReader ++ ++func SupportsHash(h crypto.Hash) bool { ++ return xcrypto.SupportsHash(h) ++} ++ ++func FIPSApprovedHash(h hash.Hash) bool { ++ return xcrypto.FIPSApprovedHash(h) ++} ++ ++func SupportsSHAKE(securityBits int) bool { return false } ++func SupportsCSHAKE(securityBits int) bool { return false } ++ ++func SupportsCurve(curve string) bool { ++ switch curve { ++ case "P-256", "P-384", "P-521", "X25519": ++ return true ++ } ++ return false ++} ++ ++func SupportsRSAOAEPLabel(label []byte) bool { ++ // CommonCrypto doesn't support labels ++ // https://github.com/microsoft/go-crypto-darwin/issues/22 ++ return len(label) == 0 ++} ++ ++func SupportsRSAPKCS1v15Encryption() bool { return true } ++ ++func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { ++ switch hash { ++ case crypto.SHA1, crypto.SHA224, crypto.SHA256, crypto.SHA384, crypto.SHA512, 0: ++ return true ++ } ++ return false ++} ++ ++type Hash = xcrypto.Hash ++ ++type SHAKE struct { ++ io.Reader ++ hash.Hash ++} ++ ++func (s *SHAKE) MarshalBinary() ([]byte, error) { panic("cryptobackend: not available") } ++func (s *SHAKE) AppendBinary(p []byte) ([]byte, error) { panic("cryptobackend: not available") } ++func (s *SHAKE) UnmarshalBinary(data []byte) error { panic("cryptobackend: not available") } ++ ++func NewMD5() hash.Hash { return xcrypto.NewMD5() } ++func NewSHA1() hash.Hash { return xcrypto.NewSHA1() } ++func NewSHA224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA256() hash.Hash { return xcrypto.NewSHA256() } ++func NewSHA384() hash.Hash { return xcrypto.NewSHA384() } ++func NewSHA512() hash.Hash { return xcrypto.NewSHA512() } ++func NewSHA512_224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA512_256() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_224() *Hash { panic("cryptobackend: not available") } ++func NewSHA3_256() *Hash { return xcrypto.NewSHA3_256() } ++func NewSHA3_384() *Hash { return xcrypto.NewSHA3_384() } ++func NewSHA3_512() *Hash { return xcrypto.NewSHA3_512() } ++ ++func NewSHAKE128() *SHAKE { panic("cryptobackend: not available") } ++func NewSHAKE256() *SHAKE { panic("cryptobackend: not available") } ++func NewCSHAKE128(N, S []byte) *SHAKE { panic("cryptobackend: not available") } ++func NewCSHAKE256(N, S []byte) *SHAKE { panic("cryptobackend: not available") } ++ ++func MD5(p []byte) (sum [16]byte) { return xcrypto.MD5(p) } ++func SHA1(p []byte) (sum [20]byte) { return xcrypto.SHA1(p) } ++func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA256(p []byte) (sum [32]byte) { return xcrypto.SHA256(p) } ++func SHA384(p []byte) (sum [48]byte) { return xcrypto.SHA384(p) } ++func SHA512(p []byte) (sum [64]byte) { return xcrypto.SHA512(p) } ++func SHA512_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA512_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SumSHA3_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SumSHA3_256(p []byte) (sum [32]byte) { return xcrypto.SumSHA3_256(p) } ++func SumSHA3_384(p []byte) (sum [48]byte) { return xcrypto.SumSHA3_384(p) } ++func SumSHA3_512(p []byte) (sum [64]byte) { return xcrypto.SumSHA3_512(p) } ++ ++func SumSHAKE128(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } ++func SumSHAKE256(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } ++ ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { ++ return xcrypto.NewHMAC(h, key) ++} ++ ++func NewAESCipher(key []byte) (cipher.Block, error) { ++ return xcrypto.NewAESCipher(key) ++} ++ ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { ++ return xcrypto.NewGCMTLS(c) ++} ++ ++func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { ++ return xcrypto.NewGCMTLS13(c) ++} ++ ++type PublicKeyECDSA = xcrypto.PublicKeyECDSA ++type PrivateKeyECDSA = xcrypto.PrivateKeyECDSA ++ ++func GenerateKeyECDSA(curve string) (X, Y, D xcrypto.BigInt, err error) { ++ return xcrypto.GenerateKeyECDSA(curve) ++} ++ ++func NewPrivateKeyECDSA(curve string, X, Y, D xcrypto.BigInt) (*xcrypto.PrivateKeyECDSA, error) { ++ return xcrypto.NewPrivateKeyECDSA(curve, X, Y, D) ++} ++ ++func NewPublicKeyECDSA(curve string, X, Y xcrypto.BigInt) (*xcrypto.PublicKeyECDSA, error) { ++ return xcrypto.NewPublicKeyECDSA(curve, X, Y) ++} ++ ++//go:linkname encodeSignature crypto/ecdsa.encodeSignature ++func encodeSignature(r, s []byte) ([]byte, error) ++ ++//go:linkname parseSignature crypto/ecdsa.parseSignature ++func parseSignature(sig []byte) (r, s []byte, err error) ++ ++func SignMarshalECDSA(priv *xcrypto.PrivateKeyECDSA, hash []byte) ([]byte, error) { ++ return xcrypto.SignMarshalECDSA(priv, hash) ++} ++ ++func VerifyECDSA(pub *xcrypto.PublicKeyECDSA, hash []byte, sig []byte) bool { ++ return xcrypto.VerifyECDSA(pub, hash, sig) ++} ++ ++func SupportsRSAPrivateKey(bits, primes int) bool { ++ return primes == 2 && SupportsRSAPublicKey(bits) ++} ++ ++func SupportsRSAPublicKey(bits int) bool { ++ return bits >= 1024 && bits%8 == 0 && bits <= 16384 ++} ++ ++func SupportsRSASaltLength(sign bool, salt int) bool { ++ // CommonCrypto doesn't support custom salt length ++ return salt == -1 ++} ++ ++type PublicKeyRSA = xcrypto.PublicKeyRSA ++type PrivateKeyRSA = xcrypto.PrivateKeyRSA ++ ++func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *xcrypto.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { ++ return xcrypto.DecryptRSAOAEP(h, priv, ciphertext, label) ++} ++ ++func DecryptRSAPKCS1(priv *xcrypto.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ return xcrypto.DecryptRSAPKCS1(priv, ciphertext) ++} ++ ++func DecryptRSANoPadding(priv *xcrypto.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ return xcrypto.DecryptRSANoPadding(priv, ciphertext) ++} ++ ++func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *xcrypto.PublicKeyRSA, msg, label []byte) ([]byte, error) { ++ return xcrypto.EncryptRSAOAEP(h, pub, msg, label) ++} ++ ++func EncryptRSAPKCS1(pub *xcrypto.PublicKeyRSA, msg []byte) ([]byte, error) { ++ return xcrypto.EncryptRSAPKCS1(pub, msg) ++} ++ ++func EncryptRSANoPadding(pub *xcrypto.PublicKeyRSA, msg []byte) ([]byte, error) { ++ return xcrypto.EncryptRSANoPadding(pub, msg) ++} ++ ++//go:linkname decodeKeyRSA crypto/rsa.decodeKey ++func decodeKeyRSA(data []byte) (N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt, err error) ++ ++//go:linkname encodeKeyRSA crypto/rsa.encodeKey ++func encodeKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt) ([]byte, error) ++ ++//go:linkname encodePublicKeyRSA crypto/rsa.encodePublicKey ++func encodePublicKeyRSA(N, E xcrypto.BigInt) ([]byte, error) ++ ++func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt, err error) { ++ data, err := xcrypto.GenerateKeyRSA(bits) ++ if err != nil { ++ return ++ } ++ return decodeKeyRSA(data) ++} ++ ++func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt) (*xcrypto.PrivateKeyRSA, error) { ++ encoded, err := encodeKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) ++ if err != nil { ++ return nil, err ++ } ++ return xcrypto.NewPrivateKeyRSA(encoded) ++} ++ ++func NewPublicKeyRSA(N, E xcrypto.BigInt) (*xcrypto.PublicKeyRSA, error) { ++ encoded, err := encodePublicKeyRSA(N, E) ++ if err != nil { ++ return nil, err ++ } ++ return xcrypto.NewPublicKeyRSA(encoded) ++} ++ ++func SignRSAPKCS1v15(priv *xcrypto.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { ++ return xcrypto.SignRSAPKCS1v15(priv, h, hashed) ++} ++ ++func SignRSAPSS(priv *xcrypto.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { ++ return xcrypto.SignRSAPSS(priv, h, hashed, saltLen) ++} ++ ++func VerifyRSAPKCS1v15(pub *xcrypto.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { ++ return xcrypto.VerifyRSAPKCS1v15(pub, h, hashed, sig) ++} ++ ++func VerifyRSAPSS(pub *xcrypto.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { ++ return xcrypto.VerifyRSAPSS(pub, h, hashed, sig, saltLen) ++} ++ ++type PrivateKeyECDH = xcrypto.PrivateKeyECDH ++type PublicKeyECDH = xcrypto.PublicKeyECDH ++ ++func ECDH(priv *xcrypto.PrivateKeyECDH, pub *xcrypto.PublicKeyECDH) ([]byte, error) { ++ return xcrypto.ECDH(priv, pub) ++} ++ ++func GenerateKeyECDH(curve string) (*xcrypto.PrivateKeyECDH, []byte, error) { ++ return xcrypto.GenerateKeyECDH(curve) ++} ++ ++func NewPrivateKeyECDH(curve string, bytes []byte) (*xcrypto.PrivateKeyECDH, error) { ++ return xcrypto.NewPrivateKeyECDH(curve, bytes) ++} ++ ++func NewPublicKeyECDH(curve string, bytes []byte) (*xcrypto.PublicKeyECDH, error) { ++ return xcrypto.NewPublicKeyECDH(curve, bytes) ++} ++ ++func SupportsTLS13KDF() bool { ++ return false ++} ++ ++func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsHKDF() bool { ++ return true ++} ++ ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { ++ return xcrypto.ExpandHKDF(h, pseudorandomKey, info, keyLength) ++} ++ ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { ++ return xcrypto.ExtractHKDF(h, secret, salt) ++} ++ ++func SupportsPBKDF2() bool { ++ return true ++} ++ ++func PBKDF2(pass, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) { ++ return xcrypto.PBKDF2(pass, salt, iter, keyLen, h) ++} ++ ++func SupportsTLS1PRF() bool { ++ return false ++} ++ ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsDESCipher() bool { ++ return true ++} ++ ++func SupportsTripleDESCipher() bool { ++ return true ++} ++ ++func NewDESCipher(key []byte) (cipher.Block, error) { ++ return xcrypto.NewDESCipher(key) ++} ++ ++func NewTripleDESCipher(key []byte) (cipher.Block, error) { ++ return xcrypto.NewTripleDESCipher(key) ++} ++ ++func SupportsRC4() bool { return true } ++ ++type RC4Cipher = xcrypto.RC4Cipher ++ ++func NewRC4Cipher(key []byte) (*RC4Cipher, error) { return xcrypto.NewRC4Cipher(key) } ++ ++func SupportsEd25519() bool { ++ return true ++} ++ ++type PublicKeyEd25519 = xcrypto.PublicKeyEd25519 ++type PrivateKeyEd25519 = xcrypto.PrivateKeyEd25519 ++ ++func GenerateKeyEd25519() (PrivateKeyEd25519, error) { ++ return xcrypto.GenerateKeyEd25519(), nil ++} ++ ++func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { ++ return xcrypto.NewPrivateKeyEd25519(priv) ++} ++ ++func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { ++ return xcrypto.NewPublicKeyEd25519(pub) ++} ++ ++func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { ++ return xcrypto.NewPrivateKeyEd25519FromSeed(seed) ++} ++ ++func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { ++ return xcrypto.SignEd25519(priv, message) ++} ++ ++func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { ++ return xcrypto.VerifyEd25519(pub, message, sig) ++} ++ ++func SupportsDSA(l, n int) bool { ++ return false ++} ++ ++func GenerateParametersDSA(l, n int) (p, q, g xcrypto.BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++ ++type PrivateKeyDSA struct{} ++type PublicKeyDSA struct{} ++ ++func GenerateKeyDSA(p, q, g xcrypto.BigInt) (x, y xcrypto.BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPrivateKeyDSA(p, q, g, x, y xcrypto.BigInt) (*PrivateKeyDSA, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPublicKeyDSA(p, q, g, y xcrypto.BigInt) (*PublicKeyDSA, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (xcrypto.BigInt, xcrypto.BigInt, error)) (r, s xcrypto.BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++ ++func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s xcrypto.BigInt, encodeSignature func(r, s xcrypto.BigInt) ([]byte, error)) bool { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsMLKEM768() bool { ++ return xcrypto.SupportsMLKEM() ++} ++ ++func SupportsMLKEM1024() bool { ++ return xcrypto.SupportsMLKEM() ++} ++ ++type DecapsulationKeyMLKEM768 = xcrypto.DecapsulationKeyMLKEM768 ++type EncapsulationKeyMLKEM768 = xcrypto.EncapsulationKeyMLKEM768 ++ ++func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { ++ return xcrypto.GenerateKeyMLKEM768() ++} ++ ++func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { ++ return xcrypto.NewDecapsulationKeyMLKEM768(seed) ++} ++ ++func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { ++ return xcrypto.NewEncapsulationKeyMLKEM768(encapsulationKey) ++} ++ ++type DecapsulationKeyMLKEM1024 = xcrypto.DecapsulationKeyMLKEM1024 ++type EncapsulationKeyMLKEM1024 = xcrypto.EncapsulationKeyMLKEM1024 ++ ++func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { ++ return xcrypto.GenerateKeyMLKEM1024() ++} ++ ++func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { ++ return xcrypto.NewDecapsulationKeyMLKEM1024(seed) ++} ++ ++func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { ++ return xcrypto.NewEncapsulationKeyMLKEM1024(encapsulationKey) ++} ++ ++func SupportsChaCha20Poly1305() bool { ++ return true ++} ++ ++func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { ++ if fips140only.Enforced() { ++ return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode") ++ } ++ return xcrypto.NewChaCha20Poly1305(key) ++} +diff --git a/src/crypto/internal/backend/backend_linux.go b/src/crypto/internal/backend/backend_linux.go +new file mode 100644 +index 00000000000000..c8c07a4beeecde +--- /dev/null ++++ b/src/crypto/internal/backend/backend_linux.go +@@ -0,0 +1,434 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++// Package openssl provides access to OpenSSLCrypto implementation functions. ++// Check the variable Enabled to find out whether OpenSSLCrypto is available. ++// If OpenSSLCrypto is not available, the functions in this package all panic. ++package backend ++ ++import ( ++ "crypto" ++ "crypto/cipher" ++ "crypto/internal/backend/fips140" ++ "crypto/internal/boring/sig" ++ "crypto/internal/fips140only" ++ "errors" ++ "hash" ++ ++ "github.com/microsoft/go-crypto-openssl/openssl" ++ "github.com/microsoft/go-crypto-openssl/osslsetup" ++) ++ ++// Enabled controls whether FIPS crypto is enabled. ++const Enabled = true ++ ++type BigInt = openssl.BigInt ++ ++func init() { ++ // Some distributions, e.g. Azure Linux 3, don't set the `fips=yes` property when running in FIPS mode, ++ // but they configure OpenSSL to use a FIPS-compliant provider (in the case of Azure Linux 3, the SCOSSL provider). ++ // In this cases, openssl.FIPS would return `false` and openssl.FIPSCapable would return `true`. ++ // We don't care about the `fips=yes` property as long as the provider is FIPS-compliant, so use ++ // osslsetup.FIPSCapable to determine whether FIPS mode is enabled. ++ if err := checkFIPS(func() bool { return osslsetup.FIPSCapable() }); err != nil { ++ // This path can be reached for the following reasons: ++ // - In OpenSSL 1, the active engine doesn't support FIPS mode. ++ // - In OpenSSL 1, the active engine supports FIPS mode, but it is not enabled. ++ // - In OpenSSL 3, the provider used by default doesn't match the `fips=yes` query. ++ panic("opensslcrypto: " + err.Error() + ": " + osslsetup.VersionText()) ++ } ++ sig.BoringCrypto() ++ fips140only.BackendApprovedHash = FIPSApprovedHash ++} ++ ++const RandReader = openssl.RandReader ++ ++func SupportsHash(h crypto.Hash) bool { ++ return openssl.SupportsHash(h) ++} ++ ++func FIPSApprovedHash(h hash.Hash) bool { ++ return openssl.FIPSApprovedHash(h) ++} ++ ++func SupportsSHAKE(securityBits int) bool { ++ return openssl.SupportsSHAKE(securityBits) ++} ++ ++func SupportsCSHAKE(securityBits int) bool { ++ return openssl.SupportsCSHAKE(securityBits) ++} ++ ++func SupportsCurve(curve string) bool { ++ return openssl.SupportsCurve(curve) ++} ++ ++func SupportsRSAOAEPLabel(label []byte) bool { return true } ++ ++func SupportsRSAPKCS1v15Encryption() bool { ++ return openssl.SupportsRSAPKCS1v15Encryption() ++} ++ ++func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { ++ return openssl.SupportsRSAPKCS1v15Signature(hash) ++} ++ ++type Hash = openssl.Hash ++type SHAKE = openssl.SHAKE ++ ++func NewMD5() hash.Hash { return openssl.NewMD5() } ++func NewSHA1() hash.Hash { return openssl.NewSHA1() } ++func NewSHA224() hash.Hash { return openssl.NewSHA224() } ++func NewSHA256() hash.Hash { return openssl.NewSHA256() } ++func NewSHA384() hash.Hash { return openssl.NewSHA384() } ++func NewSHA512() hash.Hash { return openssl.NewSHA512() } ++func NewSHA512_224() hash.Hash { return openssl.NewSHA512_224() } ++func NewSHA512_256() hash.Hash { return openssl.NewSHA512_256() } ++func NewSHA3_224() *Hash { return openssl.NewSHA3_224() } ++func NewSHA3_256() *Hash { return openssl.NewSHA3_256() } ++func NewSHA3_384() *Hash { return openssl.NewSHA3_384() } ++func NewSHA3_512() *Hash { return openssl.NewSHA3_512() } ++ ++func NewSHAKE128() *SHAKE { return openssl.NewSHAKE128() } ++func NewSHAKE256() *SHAKE { return openssl.NewSHAKE256() } ++func NewCSHAKE128(N, S []byte) *SHAKE { return openssl.NewCSHAKE128(N, S) } ++func NewCSHAKE256(N, S []byte) *SHAKE { return openssl.NewCSHAKE256(N, S) } ++ ++func MD5(p []byte) (sum [16]byte) { return openssl.MD5(p) } ++func SHA1(p []byte) (sum [20]byte) { return openssl.SHA1(p) } ++func SHA224(p []byte) (sum [28]byte) { return openssl.SHA224(p) } ++func SHA256(p []byte) (sum [32]byte) { return openssl.SHA256(p) } ++func SHA384(p []byte) (sum [48]byte) { return openssl.SHA384(p) } ++func SHA512(p []byte) (sum [64]byte) { return openssl.SHA512(p) } ++func SHA512_224(p []byte) (sum [28]byte) { return openssl.SHA512_224(p) } ++func SHA512_256(p []byte) (sum [32]byte) { return openssl.SHA512_256(p) } ++func SumSHA3_224(p []byte) (sum [28]byte) { return openssl.SumSHA3_224(p) } ++func SumSHA3_256(p []byte) (sum [32]byte) { return openssl.SumSHA3_256(p) } ++func SumSHA3_384(p []byte) (sum [48]byte) { return openssl.SumSHA3_384(p) } ++func SumSHA3_512(p []byte) (sum [64]byte) { return openssl.SumSHA3_512(p) } ++ ++func SumSHAKE128(data []byte, length int) (sum []byte) { return openssl.SumSHAKE128(data, length) } ++func SumSHAKE256(data []byte, length int) (sum []byte) { return openssl.SumSHAKE256(data, length) } ++ ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { return openssl.NewHMAC(h, key) } ++ ++func NewAESCipher(key []byte) (cipher.Block, error) { return openssl.NewAESCipher(key) } ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { return openssl.NewGCMTLS(c) } ++func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { return openssl.NewGCMTLS13(c) } ++ ++type PublicKeyECDSA = openssl.PublicKeyECDSA ++type PrivateKeyECDSA = openssl.PrivateKeyECDSA ++ ++func GenerateKeyECDSA(curve string) (X, Y, D openssl.BigInt, err error) { ++ return openssl.GenerateKeyECDSA(curve) ++} ++ ++func NewPrivateKeyECDSA(curve string, X, Y, D openssl.BigInt) (*openssl.PrivateKeyECDSA, error) { ++ return openssl.NewPrivateKeyECDSA(curve, X, Y, D) ++} ++ ++func NewPublicKeyECDSA(curve string, X, Y openssl.BigInt) (*openssl.PublicKeyECDSA, error) { ++ return openssl.NewPublicKeyECDSA(curve, X, Y) ++} ++ ++func SignMarshalECDSA(priv *openssl.PrivateKeyECDSA, hash []byte) ([]byte, error) { ++ return openssl.SignMarshalECDSA(priv, hash) ++} ++ ++func VerifyECDSA(pub *openssl.PublicKeyECDSA, hash []byte, sig []byte) bool { ++ return openssl.VerifyECDSA(pub, hash, sig) ++} ++ ++func SupportsRSAPrivateKey(bits, primes int) bool { ++ // The built-in OpenSSL 3 providers and OpenSSL 1 do support n-prime RSA keys, ++ // but SCOSSL only supports 2-prime RSA keys. ++ // Only 2-prime RSA keys are FIPS compliant, other n having compatibility ++ // and security issues. Even crypto/rsa deprecated rsa.GenerateMultiPrimeKey as of Go 1.21. ++ // Given the above reasons, we only support what SCOSSL supports. ++ return primes == 2 && SupportsRSAPublicKey(bits) ++} ++ ++func SupportsRSAPublicKey(bits int) bool { ++ min := 1024 ++ if fips140.Enabled() { ++ // The built-in OpenSSL 3 FIPS provider requires at least 2048 bits for FIPS compliance. ++ min = 2048 ++ } ++ return bits >= min && bits%8 == 0 && bits <= 16384 ++} ++ ++func SupportsRSASaltLength(sign bool, salt int) bool { ++ return true ++} ++ ++type PublicKeyRSA = openssl.PublicKeyRSA ++type PrivateKeyRSA = openssl.PrivateKeyRSA ++ ++func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *openssl.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { ++ return openssl.DecryptRSAOAEP(h, mgfHash, priv, ciphertext, label) ++} ++ ++func DecryptRSAPKCS1(priv *openssl.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ return openssl.DecryptRSAPKCS1(priv, ciphertext) ++} ++ ++func DecryptRSANoPadding(priv *openssl.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ return openssl.DecryptRSANoPadding(priv, ciphertext) ++} ++ ++func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *openssl.PublicKeyRSA, msg, label []byte) ([]byte, error) { ++ return openssl.EncryptRSAOAEP(h, mgfHash, pub, msg, label) ++} ++ ++func EncryptRSAPKCS1(pub *openssl.PublicKeyRSA, msg []byte) ([]byte, error) { ++ return openssl.EncryptRSAPKCS1(pub, msg) ++} ++ ++func EncryptRSANoPadding(pub *openssl.PublicKeyRSA, msg []byte) ([]byte, error) { ++ return openssl.EncryptRSANoPadding(pub, msg) ++} ++ ++func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv openssl.BigInt, err error) { ++ return openssl.GenerateKeyRSA(bits) ++} ++ ++func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv openssl.BigInt) (*openssl.PrivateKeyRSA, error) { ++ return openssl.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) ++} ++ ++func NewPublicKeyRSA(N, E openssl.BigInt) (*openssl.PublicKeyRSA, error) { ++ return openssl.NewPublicKeyRSA(N, E) ++} ++ ++func SignRSAPKCS1v15(priv *openssl.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { ++ return openssl.SignRSAPKCS1v15(priv, h, hashed) ++} ++ ++func SignRSAPSS(priv *openssl.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { ++ return openssl.SignRSAPSS(priv, h, hashed, saltLen) ++} ++ ++func VerifyRSAPKCS1v15(pub *openssl.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { ++ return openssl.VerifyRSAPKCS1v15(pub, h, hashed, sig) ++} ++ ++func VerifyRSAPSS(pub *openssl.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { ++ return openssl.VerifyRSAPSS(pub, h, hashed, sig, saltLen) ++} ++ ++type PublicKeyECDH = openssl.PublicKeyECDH ++type PrivateKeyECDH = openssl.PrivateKeyECDH ++ ++func ECDH(priv *openssl.PrivateKeyECDH, pub *openssl.PublicKeyECDH) ([]byte, error) { ++ return openssl.ECDH(priv, pub) ++} ++ ++func GenerateKeyECDH(curve string) (*openssl.PrivateKeyECDH, []byte, error) { ++ return openssl.GenerateKeyECDH(curve) ++} ++ ++func NewPrivateKeyECDH(curve string, bytes []byte) (*openssl.PrivateKeyECDH, error) { ++ return openssl.NewPrivateKeyECDH(curve, bytes) ++} ++ ++func NewPublicKeyECDH(curve string, bytes []byte) (*openssl.PublicKeyECDH, error) { ++ return openssl.NewPublicKeyECDH(curve, bytes) ++} ++ ++func SupportsTLS13KDF() bool { ++ return openssl.SupportsTLS13KDF() ++} ++ ++func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { ++ return openssl.ExpandTLS13KDF(h, pseudorandomKey, label, context, keyLength) ++} ++ ++func SupportsHKDF() bool { ++ return openssl.SupportsHKDF() ++} ++ ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { ++ return openssl.ExpandHKDF(h, pseudorandomKey, info, keyLength) ++} ++ ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { ++ return openssl.ExtractHKDF(h, secret, salt) ++} ++ ++func SupportsPBKDF2() bool { ++ return openssl.SupportsPBKDF2() ++} ++ ++func PBKDF2(pass, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) { ++ return openssl.PBKDF2(pass, salt, iter, keyLen, h) ++} ++ ++func SupportsTLS1PRF() bool { ++ return openssl.SupportsTLS1PRF() ++} ++ ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { ++ return openssl.TLS1PRF(result, secret, label, seed, h) ++} ++ ++func SupportsDESCipher() bool { ++ return openssl.SupportsDESCipher() ++} ++ ++func SupportsTripleDESCipher() bool { ++ return openssl.SupportsTripleDESCipher() ++} ++ ++func NewDESCipher(key []byte) (cipher.Block, error) { ++ return openssl.NewDESCipher(key) ++} ++ ++func NewTripleDESCipher(key []byte) (cipher.Block, error) { ++ return openssl.NewTripleDESCipher(key) ++} ++ ++func SupportsRC4() bool { ++ return openssl.SupportsRC4() ++} ++ ++type RC4Cipher = openssl.RC4Cipher ++ ++func NewRC4Cipher(key []byte) (*RC4Cipher, error) { return openssl.NewRC4Cipher(key) } ++ ++func SupportsEd25519() bool { return openssl.SupportsEd25519() } ++ ++type PublicKeyEd25519 = *openssl.PublicKeyEd25519 ++type PrivateKeyEd25519 = *openssl.PrivateKeyEd25519 ++ ++func GenerateKeyEd25519() (PrivateKeyEd25519, error) { ++ return openssl.GenerateKeyEd25519() ++} ++ ++// Deprecated: use NewPrivateKeyEd25519 instead. ++func NewPrivateKeyEd25119(priv []byte) (PrivateKeyEd25519, error) { ++ return openssl.NewPrivateKeyEd25519(priv) ++} ++ ++// Deprecated: use NewPublicKeyEd25519 instead. ++func NewPublicKeyEd25119(pub []byte) (PublicKeyEd25519, error) { ++ return openssl.NewPublicKeyEd25519(pub) ++} ++ ++func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { ++ return openssl.NewPrivateKeyEd25519(priv) ++} ++ ++func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { ++ return openssl.NewPublicKeyEd25519(pub) ++} ++ ++func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { ++ return openssl.NewPrivateKeyEd25519FromSeed(seed) ++} ++ ++func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { ++ return openssl.SignEd25519(priv, message) ++} ++ ++func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { ++ return openssl.VerifyEd25519(pub, message, sig) ++} ++ ++type PublicKeyDSA = openssl.PublicKeyDSA ++type PrivateKeyDSA = openssl.PrivateKeyDSA ++ ++func SupportsDSA(l, n int) bool { ++ return openssl.SupportsDSA() ++} ++ ++func GenerateParametersDSA(l, n int) (p, q, g openssl.BigInt, err error) { ++ params, err := openssl.GenerateParametersDSA(l, n) ++ return params.P, params.Q, params.G, err ++} ++ ++func GenerateKeyDSA(p, q, g openssl.BigInt) (x, y openssl.BigInt, err error) { ++ return openssl.GenerateKeyDSA(openssl.DSAParameters{P: p, Q: q, G: g}) ++} ++ ++func NewPrivateKeyDSA(p, q, g, x, y openssl.BigInt) (*openssl.PrivateKeyDSA, error) { ++ return openssl.NewPrivateKeyDSA(openssl.DSAParameters{P: p, Q: q, G: g}, x, y) ++} ++ ++func NewPublicKeyDSA(p, q, g, y openssl.BigInt) (*openssl.PublicKeyDSA, error) { ++ return openssl.NewPublicKeyDSA(openssl.DSAParameters{P: p, Q: q, G: g}, y) ++} ++ ++func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (openssl.BigInt, openssl.BigInt, error)) (r, s openssl.BigInt, err error) { ++ sig, err := openssl.SignDSA(priv, hash) ++ if err != nil { ++ return nil, nil, err ++ } ++ ++ r, s, err = parseSignature(sig) ++ if err != nil { ++ return nil, nil, err ++ } ++ ++ return openssl.BigInt(r), openssl.BigInt(s), nil ++} ++ ++func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s openssl.BigInt, encodeSignature func(r, s openssl.BigInt) ([]byte, error)) bool { ++ sig, err := encodeSignature(r, s) ++ if err != nil { ++ return false ++ } ++ ++ return openssl.VerifyDSA(pub, hashed, sig) ++} ++ ++func SupportsMLKEM768() bool { ++ return openssl.SupportsMLKEM768() ++} ++ ++func SupportsMLKEM1024() bool { ++ return openssl.SupportsMLKEM1024() ++} ++ ++type DecapsulationKeyMLKEM768 = openssl.DecapsulationKeyMLKEM768 ++type EncapsulationKeyMLKEM768 = openssl.EncapsulationKeyMLKEM768 ++ ++func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { ++ return openssl.GenerateKeyMLKEM768() ++} ++ ++func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { ++ return openssl.NewDecapsulationKeyMLKEM768(seed) ++} ++ ++func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { ++ return openssl.NewEncapsulationKeyMLKEM768(encapsulationKey) ++} ++ ++type DecapsulationKeyMLKEM1024 = openssl.DecapsulationKeyMLKEM1024 ++type EncapsulationKeyMLKEM1024 = openssl.EncapsulationKeyMLKEM1024 ++ ++func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { ++ return openssl.GenerateKeyMLKEM1024() ++} ++ ++func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { ++ return openssl.NewDecapsulationKeyMLKEM1024(seed) ++} ++ ++func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { ++ return openssl.NewEncapsulationKeyMLKEM1024(encapsulationKey) ++} ++ ++func SupportsChaCha20Poly1305() bool { ++ return openssl.SupportsChaCha20Poly1305() ++} ++ ++func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { ++ if fips140only.Enforced() { ++ return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode") ++ } ++ return openssl.NewChaCha20Poly1305(key) ++} +diff --git a/src/crypto/internal/backend/backend_test.go b/src/crypto/internal/backend/backend_test.go +new file mode 100644 +index 00000000000000..093acd82556330 +--- /dev/null ++++ b/src/crypto/internal/backend/backend_test.go +@@ -0,0 +1,56 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package backend ++ ++import ( ++ "testing" ++) ++ ++// Test that Unreachable panics. ++func TestUnreachable(t *testing.T) { ++ defer func() { ++ if Enabled { ++ if err := recover(); err == nil { ++ t.Fatal("expected Unreachable to panic") ++ } ++ } else { ++ if err := recover(); err != nil { ++ t.Fatalf("expected Unreachable to be a no-op") ++ } ++ } ++ }() ++ Unreachable() ++} ++ ++// Test that UnreachableExceptTests does not panic (this is a test). ++func TestUnreachableExceptTests(t *testing.T) { ++ UnreachableExceptTests() ++} ++ ++func TestSupportsRSAPrivateKey(t *testing.T) { ++ if !Enabled { ++ t.Skip("BoringCrypto not enabled") ++ } ++ tests := []struct { ++ bitLen int ++ numPrimes int ++ supported bool ++ }{ ++ {2048, 2, true}, ++ {3072, 2, true}, ++ {4096, 2, true}, ++ {2048, 3, false}, ++ {3072, 3, false}, ++ {4096, 3, false}, ++ } ++ for _, test := range tests { ++ t.Run("", func(t *testing.T) { ++ supported := SupportsRSAPrivateKey(test.bitLen, test.numPrimes) ++ if supported != test.supported { ++ t.Errorf("SupportsRSAPrivateKey(%d, %d) = %v; want %v", test.bitLen, test.numPrimes, supported, test.supported) ++ } ++ }) ++ } ++} +diff --git a/src/crypto/internal/backend/backend_windows.go b/src/crypto/internal/backend/backend_windows.go +new file mode 100644 +index 00000000000000..8013f189808e31 +--- /dev/null ++++ b/src/crypto/internal/backend/backend_windows.go +@@ -0,0 +1,441 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++// Package cng provides access to CNGCrypto implementation functions. ++// Check the variable Enabled to find out whether CNGCrypto is available. ++// If CNGCrypto is not available, the functions in this package all panic. ++package backend ++ ++import ( ++ "crypto" ++ "crypto/cipher" ++ "crypto/internal/boring/sig" ++ "crypto/internal/fips140only" ++ "errors" ++ "hash" ++ _ "unsafe" ++ ++ "github.com/microsoft/go-crypto-winnative/cng" ++) ++ ++func init() { ++ // Windows is considered FIPS compliant. ++ if err := checkFIPS(func() bool { return true }); err != nil { ++ panic("cngcrypto: " + err.Error()) ++ } ++ sig.BoringCrypto() ++ fips140only.BackendApprovedHash = FIPSApprovedHash ++} ++ ++// Enabled controls whether FIPS crypto is enabled. ++const Enabled = true ++ ++type BigInt = cng.BigInt ++ ++const RandReader = cng.RandReader ++ ++func SupportsHash(h crypto.Hash) bool { ++ return cng.SupportsHash(h) ++} ++ ++func FIPSApprovedHash(h hash.Hash) bool { ++ return cng.FIPSApprovedHash(h) ++} ++ ++func SupportsSHAKE(securityBits int) bool { ++ return cng.SupportsSHAKE(securityBits) ++} ++ ++func SupportsCSHAKE(securityBits int) bool { ++ return cng.SupportsSHAKE(securityBits) ++} ++ ++func SupportsCurve(curve string) bool { ++ switch curve { ++ case "P-224", "P-256", "P-384", "P-521", "X25519": ++ return true ++ } ++ return false ++} ++ ++func SupportsRSAOAEPLabel(label []byte) bool { return true } ++func SupportsRSAPKCS1v15Encryption() bool { return true } ++ ++func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { ++ // 0 and MD5SHA1 are special cases that are always supported for PKCS1v15 signatures. ++ switch hash { ++ case 0, crypto.MD5SHA1: ++ return true ++ default: ++ return cng.SupportsHash(hash) ++ } ++} ++ ++type Hash = cng.Hash ++type SHAKE = cng.SHAKE ++ ++func NewMD5() hash.Hash { return cng.NewMD5() } ++func NewSHA1() hash.Hash { return cng.NewSHA1() } ++func NewSHA224() hash.Hash { panic("cngcrypto: not available") } ++func NewSHA256() hash.Hash { return cng.NewSHA256() } ++func NewSHA384() hash.Hash { return cng.NewSHA384() } ++func NewSHA512() hash.Hash { return cng.NewSHA512() } ++func NewSHA512_224() hash.Hash { panic("cngcrypto: not available") } ++func NewSHA512_256() hash.Hash { panic("cngcrypto: not available") } ++func NewSHA3_224() *Hash { panic("cngcrypto: not available") } ++func NewSHA3_256() *Hash { return cng.NewSHA3_256() } ++func NewSHA3_384() *Hash { return cng.NewSHA3_384() } ++func NewSHA3_512() *Hash { return cng.NewSHA3_512() } ++ ++func NewSHAKE128() *SHAKE { return cng.NewSHAKE128() } ++func NewSHAKE256() *SHAKE { return cng.NewSHAKE256() } ++func NewCSHAKE128(N, S []byte) *SHAKE { return cng.NewCSHAKE128(N, S) } ++func NewCSHAKE256(N, S []byte) *SHAKE { return cng.NewCSHAKE256(N, S) } ++ ++func MD5(p []byte) (sum [16]byte) { return cng.MD5(p) } ++func SHA1(p []byte) (sum [20]byte) { return cng.SHA1(p) } ++func SHA224(p []byte) (sum [28]byte) { panic("cngcrypto: not available") } ++func SHA256(p []byte) (sum [32]byte) { return cng.SHA256(p) } ++func SHA384(p []byte) (sum [48]byte) { return cng.SHA384(p) } ++func SHA512(p []byte) (sum [64]byte) { return cng.SHA512(p) } ++func SHA512_224(p []byte) (sum [28]byte) { panic("cngcrypto: not available") } ++func SHA512_256(p []byte) (sum [32]byte) { panic("cngcrypto: not available") } ++func SumSHA3_224(p []byte) (sum [28]byte) { panic("cngcrypto: not available") } ++func SumSHA3_256(p []byte) (sum [32]byte) { return cng.SumSHA3_256(p) } ++func SumSHA3_384(p []byte) (sum [48]byte) { return cng.SumSHA3_384(p) } ++func SumSHA3_512(p []byte) (sum [64]byte) { return cng.SumSHA3_512(p) } ++ ++func SumSHAKE128(data []byte, length int) (sum []byte) { return cng.SumSHAKE128(data, length) } ++func SumSHAKE256(data []byte, length int) (sum []byte) { return cng.SumSHAKE256(data, length) } ++ ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { ++ return cng.NewHMAC(h, key) ++} ++ ++func NewAESCipher(key []byte) (cipher.Block, error) { ++ return cng.NewAESCipher(key) ++} ++ ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { ++ return cng.NewGCMTLS(c) ++} ++ ++func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { ++ return cng.NewGCMTLS13(c) ++} ++ ++type PublicKeyECDSA = cng.PublicKeyECDSA ++type PrivateKeyECDSA = cng.PrivateKeyECDSA ++ ++func GenerateKeyECDSA(curve string) (X, Y, D cng.BigInt, err error) { ++ return cng.GenerateKeyECDSA(curve) ++} ++ ++func NewPrivateKeyECDSA(curve string, X, Y, D cng.BigInt) (*cng.PrivateKeyECDSA, error) { ++ return cng.NewPrivateKeyECDSA(curve, X, Y, D) ++} ++ ++func NewPublicKeyECDSA(curve string, X, Y cng.BigInt) (*cng.PublicKeyECDSA, error) { ++ return cng.NewPublicKeyECDSA(curve, X, Y) ++} ++ ++//go:linkname encodeSignature crypto/ecdsa.encodeSignature ++func encodeSignature(r, s []byte) ([]byte, error) ++ ++//go:linkname parseSignature crypto/ecdsa.parseSignature ++func parseSignature(sig []byte) (r, s []byte, err error) ++ ++func SignMarshalECDSA(priv *cng.PrivateKeyECDSA, hash []byte) ([]byte, error) { ++ r, s, err := cng.SignECDSA(priv, hash) ++ if err != nil { ++ return nil, err ++ } ++ return encodeSignature(r, s) ++} ++ ++func VerifyECDSA(pub *cng.PublicKeyECDSA, hash []byte, sig []byte) bool { ++ rBytes, sBytes, err := parseSignature(sig) ++ if err != nil { ++ return false ++ } ++ return cng.VerifyECDSA(pub, hash, cng.BigInt(rBytes), cng.BigInt(sBytes)) ++} ++ ++func SignECDSA(priv *cng.PrivateKeyECDSA, hash []byte) (r, s cng.BigInt, err error) { ++ return cng.SignECDSA(priv, hash) ++} ++ ++func VerifyECDSARaw(pub *cng.PublicKeyECDSA, hash []byte, r, s cng.BigInt) bool { ++ return cng.VerifyECDSA(pub, hash, r, s) ++} ++ ++func SupportsRSAPrivateKey(bits, primes int) bool { ++ return primes == 2 && SupportsRSAPublicKey(bits) ++} ++ ++func SupportsRSAPublicKey(bits int) bool { ++ return bits >= 512 && bits%8 == 0 && bits <= 16384 ++} ++ ++func SupportsRSASaltLength(sign bool, salt int) bool { ++ if sign { ++ return true ++ } ++ return salt != 0 // rsa.PSSSaltLengthAuto ++} ++ ++type PublicKeyRSA = cng.PublicKeyRSA ++type PrivateKeyRSA = cng.PrivateKeyRSA ++ ++func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *cng.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { ++ return cng.DecryptRSAOAEP(h, priv, ciphertext, label) ++} ++ ++func DecryptRSAPKCS1(priv *cng.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ return cng.DecryptRSAPKCS1(priv, ciphertext) ++} ++ ++func DecryptRSANoPadding(priv *cng.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ return cng.DecryptRSANoPadding(priv, ciphertext) ++} ++ ++func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *cng.PublicKeyRSA, msg, label []byte) ([]byte, error) { ++ return cng.EncryptRSAOAEP(h, pub, msg, label) ++} ++ ++func EncryptRSAPKCS1(pub *cng.PublicKeyRSA, msg []byte) ([]byte, error) { ++ return cng.EncryptRSAPKCS1(pub, msg) ++} ++ ++func EncryptRSANoPadding(pub *cng.PublicKeyRSA, msg []byte) ([]byte, error) { ++ return cng.EncryptRSANoPadding(pub, msg) ++} ++ ++func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv cng.BigInt, err error) { ++ return cng.GenerateKeyRSA(bits) ++} ++ ++func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv cng.BigInt) (*cng.PrivateKeyRSA, error) { ++ return cng.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) ++} ++ ++func NewPublicKeyRSA(N, E cng.BigInt) (*cng.PublicKeyRSA, error) { ++ return cng.NewPublicKeyRSA(N, E) ++} ++ ++func SignRSAPKCS1v15(priv *cng.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { ++ return cng.SignRSAPKCS1v15(priv, h, hashed) ++} ++ ++func SignRSAPSS(priv *cng.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { ++ return cng.SignRSAPSS(priv, h, hashed, saltLen) ++} ++ ++func VerifyRSAPKCS1v15(pub *cng.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { ++ return cng.VerifyRSAPKCS1v15(pub, h, hashed, sig) ++} ++ ++func VerifyRSAPSS(pub *cng.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { ++ return cng.VerifyRSAPSS(pub, h, hashed, sig, saltLen) ++} ++ ++type PrivateKeyECDH = cng.PrivateKeyECDH ++type PublicKeyECDH = cng.PublicKeyECDH ++ ++func ECDH(priv *cng.PrivateKeyECDH, pub *cng.PublicKeyECDH) ([]byte, error) { ++ return cng.ECDH(priv, pub) ++} ++ ++func GenerateKeyECDH(curve string) (*cng.PrivateKeyECDH, []byte, error) { ++ return cng.GenerateKeyECDH(curve) ++} ++ ++func NewPrivateKeyECDH(curve string, bytes []byte) (*cng.PrivateKeyECDH, error) { ++ return cng.NewPrivateKeyECDH(curve, bytes) ++} ++ ++func NewPublicKeyECDH(curve string, bytes []byte) (*cng.PublicKeyECDH, error) { ++ return cng.NewPublicKeyECDH(curve, bytes) ++} ++ ++func SupportsTLS13KDF() bool { ++ return false ++} ++ ++func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsHKDF() bool { ++ return cng.SupportsHKDF() ++} ++ ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { ++ return cng.ExpandHKDF(h, pseudorandomKey, info, keyLength) ++} ++ ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { ++ return cng.ExtractHKDF(h, secret, salt) ++} ++ ++func SupportsPBKDF2() bool { return true } ++ ++func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) { ++ return cng.PBKDF2(password, salt, iter, keyLen, h) ++} ++ ++func SupportsTLS1PRF() bool { ++ return true ++} ++ ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { ++ return cng.TLS1PRF(result, secret, label, seed, h) ++} ++ ++func SupportsDESCipher() bool { ++ return true ++} ++ ++func SupportsTripleDESCipher() bool { ++ return true ++} ++ ++func NewDESCipher(key []byte) (cipher.Block, error) { ++ return cng.NewDESCipher(key) ++} ++ ++func NewTripleDESCipher(key []byte) (cipher.Block, error) { ++ return cng.NewTripleDESCipher(key) ++} ++ ++func SupportsRC4() bool { return true } ++ ++type RC4Cipher = cng.RC4Cipher ++ ++func NewRC4Cipher(key []byte) (*RC4Cipher, error) { return cng.NewRC4Cipher(key) } ++ ++func SupportsEd25519() bool { return false } ++ ++type PublicKeyEd25519 struct{} ++ ++func (k PublicKeyEd25519) Bytes() ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++type PrivateKeyEd25519 struct{} ++ ++func (k PrivateKeyEd25519) Bytes() ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func GenerateKeyEd25519() (PrivateKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { ++ panic("cryptobackend: not available") ++} ++ ++type PrivateKeyDSA = cng.PrivateKeyDSA ++type PublicKeyDSA = cng.PublicKeyDSA ++ ++func SupportsDSA(l, n int) bool { ++ // These are the only N values supported by CNG ++ return n == 160 || n == 256 ++} ++ ++func GenerateParametersDSA(l, n int) (p, q, g cng.BigInt, err error) { ++ params, err := cng.GenerateParametersDSA(l) ++ if err != nil { ++ return nil, nil, nil, err ++ } ++ return params.P, params.Q, params.G, nil ++} ++ ++func GenerateKeyDSA(p, q, g cng.BigInt) (x, y cng.BigInt, err error) { ++ return cng.GenerateKeyDSA(cng.DSAParameters{P: p, Q: q, G: g}) ++} ++ ++func NewPrivateKeyDSA(p, q, g, x, y cng.BigInt) (*cng.PrivateKeyDSA, error) { ++ return cng.NewPrivateKeyDSA(cng.DSAParameters{P: p, Q: q, G: g}, x, y) ++} ++ ++func NewPublicKeyDSA(p, q, g, y cng.BigInt) (*cng.PublicKeyDSA, error) { ++ return cng.NewPublicKeyDSA(cng.DSAParameters{P: p, Q: q, G: g}, y) ++} ++ ++func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (cng.BigInt, cng.BigInt, error)) (r, s cng.BigInt, err error) { ++ return cng.SignDSA(priv, hash) ++} ++ ++func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s cng.BigInt, encodeSignature func(r, s cng.BigInt) ([]byte, error)) bool { ++ return cng.VerifyDSA(pub, hashed, r, s) ++} ++ ++func SupportsMLKEM768() bool { ++ return cng.SupportsMLKEM() ++} ++ ++func SupportsMLKEM1024() bool { ++ return cng.SupportsMLKEM() ++} ++ ++type DecapsulationKeyMLKEM768 = cng.DecapsulationKeyMLKEM768 ++type EncapsulationKeyMLKEM768 = cng.EncapsulationKeyMLKEM768 ++ ++func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { ++ return cng.GenerateKeyMLKEM768() ++} ++ ++func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { ++ return cng.NewDecapsulationKeyMLKEM768(seed) ++} ++ ++func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { ++ return cng.NewEncapsulationKeyMLKEM768(encapsulationKey) ++} ++ ++type DecapsulationKeyMLKEM1024 = cng.DecapsulationKeyMLKEM1024 ++type EncapsulationKeyMLKEM1024 = cng.EncapsulationKeyMLKEM1024 ++ ++func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { ++ return cng.GenerateKeyMLKEM1024() ++} ++ ++func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { ++ return cng.NewDecapsulationKeyMLKEM1024(seed) ++} ++ ++func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { ++ return cng.NewEncapsulationKeyMLKEM1024(encapsulationKey) ++} ++ ++func SupportsChaCha20Poly1305() bool { ++ return cng.SupportsChaCha20Poly1305() ++} ++ ++func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { ++ if fips140only.Enforced() { ++ return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode") ++ } ++ return cng.NewChaCha20Poly1305(key) ++} +diff --git a/src/crypto/internal/backend/bbig/big.go b/src/crypto/internal/backend/bbig/big.go +new file mode 100644 +index 00000000000000..20251a290dc2e0 +--- /dev/null ++++ b/src/crypto/internal/backend/bbig/big.go +@@ -0,0 +1,17 @@ ++// Copyright 2022 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !goexperiment.systemcrypto ++ ++package bbig ++ ++import "math/big" ++ ++func Enc(b *big.Int) []uint { ++ return nil ++} ++ ++func Dec(b []uint) *big.Int { ++ return nil ++} +diff --git a/src/crypto/internal/backend/bbig/big_darwin.go b/src/crypto/internal/backend/bbig/big_darwin.go +new file mode 100644 +index 00000000000000..889f2ff7c703d8 +--- /dev/null ++++ b/src/crypto/internal/backend/bbig/big_darwin.go +@@ -0,0 +1,12 @@ ++// Copyright 2022 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package bbig ++ ++import "github.com/microsoft/go-crypto-darwin/bbig" ++ ++var Enc = bbig.Enc ++var Dec = bbig.Dec +diff --git a/src/crypto/internal/backend/bbig/big_linux.go b/src/crypto/internal/backend/bbig/big_linux.go +new file mode 100644 +index 00000000000000..1b515fe6244a52 +--- /dev/null ++++ b/src/crypto/internal/backend/bbig/big_linux.go +@@ -0,0 +1,12 @@ ++// Copyright 2022 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package bbig ++ ++import "github.com/microsoft/go-crypto-openssl/bbig" ++ ++var Enc = bbig.Enc ++var Dec = bbig.Dec +diff --git a/src/crypto/internal/backend/bbig/big_windows.go b/src/crypto/internal/backend/bbig/big_windows.go +new file mode 100644 +index 00000000000000..f2c21a88bff471 +--- /dev/null ++++ b/src/crypto/internal/backend/bbig/big_windows.go +@@ -0,0 +1,12 @@ ++// Copyright 2022 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package bbig ++ ++import "github.com/microsoft/go-crypto-winnative/cng/bbig" ++ ++var Enc = bbig.Enc ++var Dec = bbig.Dec +diff --git a/src/crypto/internal/backend/common.go b/src/crypto/internal/backend/common.go +new file mode 100644 +index 00000000000000..c5f983f68739aa +--- /dev/null ++++ b/src/crypto/internal/backend/common.go +@@ -0,0 +1,51 @@ ++// Copyright 2022 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package backend ++ ++import ( ++ "crypto/internal/backend/internal/fips140state" ++ "crypto/internal/boring/sig" ++ "runtime" ++) ++ ++func checkFIPS(fips func() bool) error { ++ return fips140state.Check(Enabled, fips) ++} ++ ++// Unreachable marks code that should be unreachable ++// when backend is in use. ++func Unreachable() { ++ if Enabled { ++ panic("cryptobackend: invalid code execution") ++ } else { ++ // Code that's unreachable is exactly the code ++ // we want to detect for reporting standard Go crypto. ++ sig.StandardCrypto() ++ } ++} ++ ++// Provided by runtime.crypto_backend_runtime_arg0 to avoid os import. ++func runtime_arg0() string ++ ++func hasSuffix(s, t string) bool { ++ return len(s) > len(t) && s[len(s)-len(t):] == t ++} ++ ++// UnreachableExceptTests marks code that should be unreachable ++// when backend is in use. It panics. ++func UnreachableExceptTests() { ++ // runtime_arg0 is not supported on windows. ++ // We are going through the same code patch on linux, ++ // so if we are unintentionally calling an 'unreachable' function, ++ // we will catch it there. ++ if Enabled && runtime.GOOS != "windows" { ++ name := runtime_arg0() ++ // If ran on Windows we'd need to allow _test.exe and .test.exe as well. ++ if !hasSuffix(name, "_test") && !hasSuffix(name, ".test") { ++ println("cryptobackend: unexpected code execution in", name) ++ panic("cryptobackend: invalid code execution") ++ } ++ } ++} +diff --git a/src/crypto/internal/backend/fips140/fips140.go b/src/crypto/internal/backend/fips140/fips140.go +new file mode 100644 +index 00000000000000..436cb00f359bcf +--- /dev/null ++++ b/src/crypto/internal/backend/fips140/fips140.go +@@ -0,0 +1,15 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package fips140 ++ ++import ( ++ "crypto/internal/backend/internal/fips140state" ++) ++ ++// Enabled reports whether FIPS 140 mode is enabled by using GODEBUG, GOFIPS, ++// GOLANG_FIPS, or any platform-specific mechanism. ++func Enabled() bool { ++ return fips140state.Enabled() ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/state_test.go b/src/crypto/internal/backend/internal/fips140state/state_test.go +new file mode 100644 +index 00000000000000..254a27594b578a +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/state_test.go +@@ -0,0 +1,75 @@ ++// Copyright 2026 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package fips140state ++ ++import ( ++ "testing" ++) ++ ++// fakeEnv returns a getenv function that serves lookups from m, with the ++// same (value, ok) semantics as [syscall.Getenv]. A key present in m with an ++// empty string value represents a set-but-empty environment variable. ++func fakeEnv(m map[string]string) func(string) (string, bool) { ++ return func(key string) (string, bool) { ++ v, ok := m[key] ++ return v, ok ++ } ++} ++ ++// TestDetect exercises the FIPS 140 detection logic in isolation, without ++// depending on process environment variables or the host's kernel FIPS ++// configuration. This lets us cover combinations that would otherwise be ++// impossible to test on a single machine, such as the kernel FIPS flag ++// being set or not set for the same test run. ++func TestDetect(t *testing.T) { ++ tests := []struct { ++ name string ++ godebug string ++ env map[string]string ++ systemFIPS bool ++ wantEnabled bool ++ wantMessage string ++ }{ ++ // GODEBUG=fips140 takes precedence over everything else. ++ {name: "GODEBUG=on", godebug: "on", wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=on"}, ++ {name: "GODEBUG=only", godebug: "only", wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=only"}, ++ {name: "GODEBUG=debug", godebug: "debug", wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=debug"}, ++ {name: "GODEBUG=on beats GOFIPS=0 and kernel FIPS", godebug: "on", env: map[string]string{"GOFIPS": "0"}, systemFIPS: true, wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=on"}, ++ {name: "GODEBUG=off beats kernel FIPS", godebug: "off", systemFIPS: true, wantEnabled: false, wantMessage: "environment variable GODEBUG=fips140=off"}, ++ {name: "GODEBUG=off beats GOFIPS=1", godebug: "off", env: map[string]string{"GOFIPS": "1"}, wantEnabled: false, wantMessage: "environment variable GODEBUG=fips140=off"}, ++ ++ // GOFIPS=1 enables FIPS when GODEBUG is unset/empty/unrecognized. ++ {name: "GOFIPS=1", env: map[string]string{"GOFIPS": "1"}, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, ++ {name: "GOFIPS=1 beats kernel FIPS off", env: map[string]string{"GOFIPS": "1"}, systemFIPS: false, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, ++ {name: "GODEBUG unrecognized, GOFIPS=1", godebug: "bogus", env: map[string]string{"GOFIPS": "1"}, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, ++ ++ // Non-"1" values of GOFIPS are ignored and fall through. ++ {name: "GOFIPS=0 falls through to kernel FIPS on", env: map[string]string{"GOFIPS": "0"}, systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, ++ {name: "GOFIPS=0 falls through to kernel FIPS off", env: map[string]string{"GOFIPS": "0"}, systemFIPS: false, wantEnabled: false, wantMessage: ""}, ++ {name: "GOFIPS empty falls through to kernel FIPS on", env: map[string]string{"GOFIPS": ""}, systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, ++ {name: "GOFIPS=garbage falls through", env: map[string]string{"GOFIPS": "garbage"}, systemFIPS: false, wantEnabled: false, wantMessage: ""}, ++ ++ // GOLANG_FIPS behaves the same as GOFIPS. ++ {name: "GOLANG_FIPS=1", env: map[string]string{"GOLANG_FIPS": "1"}, wantEnabled: true, wantMessage: "environment variable GOLANG_FIPS=1"}, ++ {name: "GOFIPS=1 beats GOLANG_FIPS=0", env: map[string]string{"GOFIPS": "1", "GOLANG_FIPS": "0"}, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, ++ {name: "GOLANG_FIPS=0 falls through to kernel FIPS on", env: map[string]string{"GOLANG_FIPS": "0"}, systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, ++ ++ // With nothing set, the kernel FIPS flag controls the result. ++ {name: "nothing set, kernel FIPS on", systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, ++ {name: "nothing set, kernel FIPS off", systemFIPS: false, wantEnabled: false, wantMessage: ""}, ++ } ++ ++ for _, tc := range tests { ++ t.Run(tc.name, func(t *testing.T) { ++ systemFIPS := func() bool { return tc.systemFIPS } ++ gotEnabled, gotMessage := detect(tc.godebug, fakeEnv(tc.env), systemFIPS) ++ if gotEnabled != tc.wantEnabled || gotMessage != tc.wantMessage { ++ t.Errorf("detect(godebug=%q, env=%v, systemFIPS=%v) = (%v, %q), want (%v, %q)", ++ tc.godebug, tc.env, tc.systemFIPS, ++ gotEnabled, gotMessage, tc.wantEnabled, tc.wantMessage) ++ } ++ }) ++ } ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/nosystemcrypto.go b/src/crypto/internal/backend/internal/fips140state/nosystemcrypto.go +new file mode 100644 +index 00000000000000..28ec07f7b20eaf +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/nosystemcrypto.go +@@ -0,0 +1,11 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !goexperiment.systemcrypto ++ ++package fips140state ++ ++func systemFIPSMode() bool { ++ return false ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/systemfips_darwin.go b/src/crypto/internal/backend/internal/fips140state/systemfips_darwin.go +new file mode 100644 +index 00000000000000..a6fba27162a6ef +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/systemfips_darwin.go +@@ -0,0 +1,11 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package fips140state ++ ++func systemFIPSMode() bool { ++ return false ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/systemfips_linux.go b/src/crypto/internal/backend/internal/fips140state/systemfips_linux.go +new file mode 100644 +index 00000000000000..051d709e557061 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/systemfips_linux.go +@@ -0,0 +1,57 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package fips140state ++ ++import ( ++ _ "crypto/internal/backend/internal/opensslsetup" ++ "syscall" ++ ++ "github.com/microsoft/go-crypto-openssl/osslsetup" ++) ++ ++// systemFIPSMode reports whether the system is in FIPS mode. ++// It first checks the kernel, and if that is not available, it checks the ++// OpenSSL library. ++func systemFIPSMode() bool { ++ if kernelFIPSMode() { ++ return true ++ } ++ return osslsetup.FIPS() ++} ++ ++// kernelFIPSMode reports whether the kernel is in FIPS mode. ++func kernelFIPSMode() bool { ++ var fd int ++ for { ++ var err error ++ fd, err = syscall.Open("/proc/sys/crypto/fips_enabled", syscall.O_RDONLY, 0) ++ if err == nil { ++ break ++ } ++ switch err { ++ case syscall.EINTR: ++ continue ++ case syscall.ENOENT: ++ return false ++ default: ++ // If there is an error reading we could either panic or assume FIPS is not enabled. ++ // Panicking would be too disruptive for apps that don't require FIPS. ++ // If an app wants to be 100% sure that is running in FIPS mode ++ // it should use fips140.Enabled() or GODEBUG=fips140=1. ++ return false ++ } ++ } ++ defer syscall.Close(fd) ++ var tmp [1]byte ++ n, err := syscall.Read(fd, tmp[:]) ++ if n != 1 || err != nil { ++ // We return false instead of panicing for the same reason as before. ++ return false ++ } ++ // fips_enabled can be either '0' or '1'. ++ return tmp[0] == '1' ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/systemfips_windows.go b/src/crypto/internal/backend/internal/fips140state/systemfips_windows.go +new file mode 100644 +index 00000000000000..2ef633391eb9ab +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/systemfips_windows.go +@@ -0,0 +1,33 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package fips140state ++ ++import ( ++ "internal/syscall/windows/sysdll" ++ "syscall" ++ "unsafe" ++) ++ ++// Don't use github.com/microsoft/go-crypto-winnative here. ++// The fips140 package should have minimal dependencies. ++// Also, don't directly query the system FIPS mode from the registry, ++// there are some no-longer documented legacy entries that can enable FIPS mode, ++// and BCryptGetFipsAlgorithmMode supports them all. ++var ( ++ bcrypt = syscall.MustLoadDLL(sysdll.Add("bcrypt.dll")) ++ ++ bcryptGetFipsAlgorithmMode = bcrypt.MustFindProc("BCryptGetFipsAlgorithmMode") ++) ++ ++func systemFIPSMode() bool { ++ var enabled uint32 ++ ret, _, _ := bcryptGetFipsAlgorithmMode.Call(uintptr(unsafe.Pointer(&enabled))) ++ if ret != 0 { ++ return false ++ } ++ return enabled != 0 ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/state.go b/src/crypto/internal/backend/internal/fips140state/state.go +new file mode 100644 +index 00000000000000..8b16a033e83ef8 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/state.go +@@ -0,0 +1,91 @@ ++// Copyright 2026 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package fips140state ++ ++import ( ++ "errors" ++ "internal/godebug" ++ "runtime" ++ "syscall" ++) ++ ++var fips140GODEBUG = godebug.New("fips140") ++ ++var enabled bool ++ ++// message is a human-readable message about how [Enabled] was set. ++var message string ++ ++func init() { ++ // TODO: Decide which environment variable to use. ++ // See https://github.com/microsoft/go/issues/397. ++ enabled, message = detect(fips140GODEBUG.Value(), syscall.Getenv, systemFIPSMode) ++} ++ ++func Enabled() bool { ++ return enabled ++} ++ ++func Check(backendEnabled bool, fips func() bool) error { ++ if isRequireFIPS { ++ if isSkipFIPSCheck { ++ panic("the 'requirefips' build tag is enabled, but it conflicts " + ++ "with the 'ms_skipfipscheck' build tag") ++ } ++ message = "requirefips tag set" ++ enabled = true ++ } ++ if isSkipFIPSCheck || !enabled { ++ return nil ++ } ++ if !backendEnabled { ++ if runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin" { ++ return errors.New("FIPS mode requested (" + message + ") but no crypto backend is supported on " + runtime.GOOS) ++ } ++ return errors.New("FIPS mode requested (" + message + ") but no supported crypto backend is enabled") ++ } ++ if !fips() { ++ return errors.New("FIPS mode requested (" + message + ") but not available") ++ } ++ return nil ++} ++ ++// detect reports whether FIPS 140 mode should be enabled and returns a ++// human-readable message describing how the decision was made. ++// ++// godebug is the value of the fips140 GODEBUG setting. getenv is used to look ++// up the GOFIPS and GOLANG_FIPS environment variables and mirrors the ++// semantics of [syscall.Getenv]. systemFIPS reports whether the platform ++// indicates that FIPS mode should be enabled (e.g. the Linux kernel FIPS flag). ++// ++// The inputs are taken as parameters, rather than read directly, to make the ++// detection logic easy to test without depending on process state. ++func detect(godebug string, getenv func(string) (string, bool), systemFIPS func() bool) (enabled bool, message string) { ++ switch godebug { ++ case "on", "only", "debug": ++ return true, "environment variable GODEBUG=fips140=" + godebug ++ case "off": ++ // GODEBUG=fips140=off explicitly disables FIPS mode and bypasses ++ // the platform-specific FIPS detection (e.g. the Linux kernel FIPS flag). ++ // This is the only supported way to skip the platform FIPS detection. ++ return false, "environment variable GODEBUG=fips140=off" ++ } ++ // Only "1" is a meaningful value for GOFIPS and GOLANG_FIPS. Any other ++ // value (including "0" and the empty string) is treated as if the ++ // variable were unset, to match the documented behavior and to avoid ++ // silently bypassing the platform FIPS detection due to a typo or ++ // accidental setting. To explicitly disable FIPS mode and skip the ++ // platform FIPS detection, use GODEBUG=fips140=off. ++ if v, ok := getenv("GOFIPS"); ok && v == "1" { ++ return true, "environment variable GOFIPS=1" ++ } ++ if v, ok := getenv("GOLANG_FIPS"); ok && v == "1" { ++ return true, "environment variable GOLANG_FIPS=1" ++ } ++ if systemFIPS() { ++ return true, "system FIPS mode" ++ } ++ return false, "" ++} +diff --git a/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go +new file mode 100644 +index 00000000000000..350c8ee7fa2bc6 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go +@@ -0,0 +1,68 @@ ++// Copyright 2025 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++// opensslsetup is a package that initializes the OpenSSL library. ++// It doesn't export any symbol, but blank importing it has the ++// side effect of initializing the OpenSSL library. ++package opensslsetup ++ ++import ( ++ "syscall" ++ ++ "github.com/microsoft/go-crypto-openssl/osslsetup" ++) ++ ++// knownVersions is a list of supported and well-known libcrypto.so suffixes in decreasing version order. ++// FreeBSD library version numbering does not directly align to the version of osslsetup. ++// Its preferred search order is 11 -> 111. ++var knownVersions = [...]string{"3", "1.1", "11", "111"} ++ ++const lcryptoPrefix = "libcrypto.so." ++ ++func init() { ++ lib := library() ++ if err := osslsetup.Init(lib); err != nil { ++ panic("opensslcrypto: can't initialize OpenSSL " + lib + ": " + err.Error()) ++ } ++} ++ ++// library returns the name of the OpenSSL library to use. ++// It first checks the environment variable GO_OPENSSL_VERSION_OVERRIDE. ++// If that is not set, it searches a well-known list of library names. ++// If no library is found, it returns "libcrypto.so". ++func library() string { ++ if version, _ := syscall.Getenv("GO_OPENSSL_VERSION_OVERRIDE"); version != "" { ++ return lcryptoPrefix + version ++ } ++ if lib := searchKnownLibrary(); lib != "" { ++ return lib ++ } ++ return lcryptoPrefix[:len(lcryptoPrefix)-1] // no version found, try without version suffix ++} ++ ++// checkVersion is a variable that holds the osslsetup.CheckVersion function. ++// It is initialized in the init function to allow overriding in tests. ++var checkVersion = osslsetup.CheckVersion ++ ++// searchKnownLibrary returns the name of the highest available FIPS-enabled version of OpenSSL ++// using the known library suffixes. ++// If no FIPS-enabled version is found, it returns the name of the highest available version. ++// If no version is found, it returns an empty string. ++func searchKnownLibrary() string { ++ var lcryptoFallback string ++ for _, v := range knownVersions { ++ lcryptoCandidate := lcryptoPrefix + v ++ if exists, fips := checkVersion(lcryptoCandidate); exists { ++ if fips { ++ return lcryptoCandidate ++ } ++ if lcryptoFallback == "" { ++ lcryptoFallback = lcryptoCandidate ++ } ++ } ++ } ++ return lcryptoFallback ++} +diff --git a/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go +new file mode 100644 +index 00000000000000..2e45358029f484 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go +@@ -0,0 +1,92 @@ ++// Copyright 2025 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto && cgo ++ ++package opensslsetup ++ ++import "testing" ++ ++func mockCheckVersion(t *testing.T, fn func(string) (bool, bool)) { ++ original := checkVersion ++ t.Cleanup(func() { ++ checkVersion = original ++ }) ++ checkVersion = fn ++} ++ ++func assertLibrary(t *testing.T, expected string) { ++ if result := library(); result != expected { ++ t.Errorf("expected %s, got %s", expected, result) ++ } ++} ++ ++func TestLibraryWithEnvOverride(t *testing.T) { ++ t.Setenv("GO_OPENSSL_VERSION_OVERRIDE", "1.1") ++ mockCheckVersion(t, func(s string) (bool, bool) { return false, false }) ++ assertLibrary(t, "libcrypto.so.1.1") ++} ++ ++func TestLibraryWithKnownVersion(t *testing.T) { ++ t.Setenv("GO_OPENSSL_VERSION_OVERRIDE", "") ++ ++ const maxLib = "libcrypto.so.3" ++ ++ t.Run("AllExistsNoneFIPS", func(t *testing.T) { ++ mockCheckVersion(t, func(s string) (bool, bool) { ++ return true, false ++ }) ++ for _, v := range knownVersions { ++ t.Run(v, func(t *testing.T) { ++ assertLibrary(t, maxLib) ++ }) ++ } ++ }) ++ ++ t.Run("OnlyOneExists", func(t *testing.T) { ++ for _, v := range knownVersions { ++ t.Run(v, func(t *testing.T) { ++ expected := "libcrypto.so." + v ++ mockCheckVersion(t, func(s string) (bool, bool) { ++ if s == expected { ++ return true, false ++ } ++ return false, false ++ }) ++ assertLibrary(t, expected) ++ }) ++ } ++ }) ++ ++ t.Run("AllExistsOnlyOneFIPS", func(t *testing.T) { ++ fipsLib := "libcrypto.so.1.1" ++ mockCheckVersion(t, func(s string) (bool, bool) { ++ return true, s == fipsLib ++ }) ++ for _, v := range knownVersions { ++ t.Run(v, func(t *testing.T) { ++ assertLibrary(t, fipsLib) ++ }) ++ } ++ }) ++ ++ t.Run("AllExistsAndAreFIPS", func(t *testing.T) { ++ mockCheckVersion(t, func(s string) (bool, bool) { ++ return true, true ++ }) ++ for _, v := range knownVersions { ++ t.Run(v, func(t *testing.T) { ++ assertLibrary(t, maxLib) ++ }) ++ } ++ }) ++} ++ ++func TestLibraryNoVersionFound(t *testing.T) { ++ t.Setenv("GO_OPENSSL_VERSION_OVERRIDE", "") ++ mockCheckVersion(t, func(string) (bool, bool) { ++ return false, false ++ }) ++ assertLibrary(t, "libcrypto.so") ++} +diff --git a/src/crypto/internal/backend/internal/opensslsetup/stub.go b/src/crypto/internal/backend/internal/opensslsetup/stub.go +new file mode 100644 +index 00000000000000..19fd29e19e7b96 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/opensslsetup/stub.go +@@ -0,0 +1,8 @@ ++// Copyright 2025 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++// Placeholder to allow the opensslsetup package to be imported ++// without cgo enabled or without goexperiment.systemcrypto on linux. ++ ++package opensslsetup +diff --git a/src/crypto/internal/backend/internal/fips140state/isrequirefips.go b/src/crypto/internal/backend/internal/fips140state/isrequirefips.go +new file mode 100644 +index 00000000000000..122185bb417d3c +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/isrequirefips.go +@@ -0,0 +1,9 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build requirefips ++ ++package fips140state ++ ++const isRequireFIPS = true +diff --git a/src/crypto/internal/backend/nobackend.go b/src/crypto/internal/backend/nobackend.go +new file mode 100644 +index 00000000000000..84a73fc5c2d523 +--- /dev/null ++++ b/src/crypto/internal/backend/nobackend.go +@@ -0,0 +1,375 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !goexperiment.systemcrypto ++ ++package backend ++ ++import ( ++ "crypto" ++ "crypto/cipher" ++ "hash" ++ "io" ++) ++ ++func init() { ++ if err := checkFIPS(func() bool { return false }); err != nil { ++ panic(err) ++ } ++} ++ ++const Enabled = false ++ ++type BigInt = []uint ++ ++type randReader int ++ ++func (randReader) Read(b []byte) (int, error) { panic("cryptobackend: not available") } ++ ++const RandReader = randReader(0) ++ ++func SupportsHash(h crypto.Hash) bool { panic("cryptobackend: not available") } ++func FIPSApprovedHash(h hash.Hash) bool { panic("cryptobackend: not available") } ++ ++func SupportsSHAKE(securityBits int) bool { panic("cryptobackend: not available") } ++func SupportsCSHAKE(securityBits int) bool { panic("cryptobackend: not available") } ++ ++func SupportsCurve(curve string) bool { panic("cryptobackend: not available") } ++func SupportsRSAOAEPLabel(label []byte) bool { panic("cryptobackend: not available") } ++func SupportsRSAPKCS1v15Encryption() bool { panic("cryptobackend: not available") } ++func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { panic("cryptobackend: not available") } ++ ++type Hash struct { ++ hash.Cloner ++} ++ ++func (d *Hash) MarshalBinary() ([]byte, error) { panic("cryptobackend: not available") } ++func (d *Hash) AppendBinary(p []byte) ([]byte, error) { panic("cryptobackend: not available") } ++func (d *Hash) UnmarshalBinary(data []byte) error { panic("cryptobackend: not available") } ++ ++type SHAKE struct { ++ io.Reader ++ hash.Hash ++} ++ ++func (s *SHAKE) MarshalBinary() ([]byte, error) { panic("cryptobackend: not available") } ++func (s *SHAKE) AppendBinary(p []byte) ([]byte, error) { panic("cryptobackend: not available") } ++func (s *SHAKE) UnmarshalBinary(data []byte) error { panic("cryptobackend: not available") } ++ ++func NewMD5() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA1() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA256() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA384() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA512() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA512_224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA512_256() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_224() *Hash { panic("cryptobackend: not available") } ++func NewSHA3_256() *Hash { panic("cryptobackend: not available") } ++func NewSHA3_384() *Hash { panic("cryptobackend: not available") } ++func NewSHA3_512() *Hash { panic("cryptobackend: not available") } ++ ++func NewSHAKE128() *SHAKE { panic("cryptobackend: not available") } ++func NewSHAKE256() *SHAKE { panic("cryptobackend: not available") } ++func NewCSHAKE128(N, S []byte) *SHAKE { panic("cryptobackend: not available") } ++func NewCSHAKE256(N, S []byte) *SHAKE { panic("cryptobackend: not available") } ++ ++func MD5(p []byte) (sum [16]byte) { panic("cryptobackend: not available") } ++func SHA1(p []byte) (sum [20]byte) { panic("cryptobackend: not available") } ++func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SHA384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } ++func SHA512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } ++func SHA512_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA512_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SumSHA3_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SumSHA3_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SumSHA3_384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } ++func SumSHA3_512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } ++ ++func SumSHAKE128(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } ++func SumSHAKE256(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } ++ ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("cryptobackend: not available") } ++ ++func NewAESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } ++func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } ++ ++type PublicKeyECDSA struct{ _ int } ++type PrivateKeyECDSA struct{ _ int } ++ ++func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++func NewPrivateKeyECDSA(curve string, X, Y, D BigInt) (*PrivateKeyECDSA, error) { ++ panic("cryptobackend: not available") ++} ++func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) { ++ panic("cryptobackend: not available") ++} ++func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool { ++ panic("cryptobackend: not available") ++} ++func SupportsRSAPrivateKey(bits, primes int) bool { panic("cryptobackend: not available") } ++func SupportsRSAPublicKey(bits int) bool { panic("cryptobackend: not available") } ++func SupportsRSASaltLength(sign bool, salt int) bool { panic("cryptobackend: not available") } ++ ++type PublicKeyRSA struct{ _ int } ++type PrivateKeyRSA struct{ _ int } ++ ++func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv BigInt) (*PrivateKeyRSA, error) { ++ panic("cryptobackend: not available") ++} ++func NewPublicKeyRSA(N, E BigInt) (*PublicKeyRSA, error) { ++ panic("cryptobackend: not available") ++} ++func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { ++ panic("cryptobackend: not available") ++} ++func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { ++ panic("cryptobackend: not available") ++} ++ ++type PublicKeyECDH struct{} ++type PrivateKeyECDH struct{} ++ ++func ECDH(*PrivateKeyECDH, *PublicKeyECDH) ([]byte, error) { panic("cryptobackend: not available") } ++func GenerateKeyECDH(string) (*PrivateKeyECDH, []byte, error) { panic("cryptobackend: not available") } ++func NewPrivateKeyECDH(string, []byte) (*PrivateKeyECDH, error) { ++ panic("cryptobackend: not available") ++} ++func NewPublicKeyECDH(string, []byte) (*PublicKeyECDH, error) { panic("cryptobackend: not available") } ++func (*PublicKeyECDH) Bytes() []byte { panic("cryptobackend: not available") } ++func (*PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) { panic("cryptobackend: not available") } ++ ++func SupportsTLS13KDF() bool { panic("cryptobackend: not available") } ++ ++func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsHKDF() bool { panic("cryptobackend: not available") } ++ ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsPBKDF2() bool { panic("cryptobackend: not available") } ++ ++func PBKDF2(password, salt []byte, iter, keyLen int, fh func() hash.Hash) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsTLS1PRF() bool { panic("cryptobackend: not available") } ++ ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsDESCipher() bool { panic("cryptobackend: not available") } ++ ++func SupportsTripleDESCipher() bool { panic("cryptobackend: not available") } ++ ++func NewDESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } ++ ++func NewTripleDESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } ++ ++func SupportsRC4() bool { panic("cryptobackend: not available") } ++ ++type RC4Cipher struct{} ++ ++func (c *RC4Cipher) Reset() { panic("cryptobackend: not available") } ++func (c *RC4Cipher) XORKeyStream(dst, src []byte) { panic("cryptobackend: not available") } ++ ++func NewRC4Cipher(key []byte) (*RC4Cipher, error) { panic("cryptobackend: not available") } ++ ++func SupportsEd25519() bool { panic("cryptobackend: not available") } ++ ++type PublicKeyEd25519 struct{} ++ ++func (k PublicKeyEd25519) Bytes() ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++type PrivateKeyEd25519 struct{} ++ ++func (k PrivateKeyEd25519) Bytes() ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func GenerateKeyEd25519() (PrivateKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsDSA(l, n int) bool { ++ panic("cryptobackend: not available") ++} ++ ++func GenerateParametersDSA(l, n int) (p, q, g BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++ ++type PublicKeyDSA struct{ _ int } ++type PrivateKeyDSA struct{ _ int } ++ ++func GenerateKeyDSA(p, q, g BigInt) (x, y BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPrivateKeyDSA(p, q, g, x, y BigInt) (*PrivateKeyDSA, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewPublicKeyDSA(p, q, g, y BigInt) (*PublicKeyDSA, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (BigInt, BigInt, error)) (r, s BigInt, err error) { ++ panic("cryptobackend: not available") ++} ++ ++func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s BigInt, encodeSignature func(r, s BigInt) ([]byte, error)) bool { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsMLKEM768() bool { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsMLKEM1024() bool { ++ panic("cryptobackend: not available") ++} ++ ++type DecapsulationKeyMLKEM768 struct{} ++type EncapsulationKeyMLKEM768 struct{} ++ ++func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { ++ panic("cryptobackend: not available") ++} ++ ++func (dk DecapsulationKeyMLKEM768) Bytes() []byte { ++ panic("cryptobackend: not available") ++} ++ ++func (dk DecapsulationKeyMLKEM768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { ++ panic("cryptobackend: not available") ++} ++ ++func (dk DecapsulationKeyMLKEM768) EncapsulationKey() EncapsulationKeyMLKEM768 { ++ panic("cryptobackend: not available") ++} ++ ++func (ek EncapsulationKeyMLKEM768) Bytes() []byte { ++ panic("cryptobackend: not available") ++} ++ ++func (ek EncapsulationKeyMLKEM768) Encapsulate() (sharedKey, ciphertext []byte) { ++ panic("cryptobackend: not available") ++} ++ ++type DecapsulationKeyMLKEM1024 struct{} ++type EncapsulationKeyMLKEM1024 struct{} ++ ++func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { ++ panic("cryptobackend: not available") ++} ++ ++func (dk DecapsulationKeyMLKEM1024) Bytes() []byte { ++ panic("cryptobackend: not available") ++} ++ ++func (dk DecapsulationKeyMLKEM1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { ++ panic("cryptobackend: not available") ++} ++ ++func (dk DecapsulationKeyMLKEM1024) EncapsulationKey() EncapsulationKeyMLKEM1024 { ++ panic("cryptobackend: not available") ++} ++ ++func (ek EncapsulationKeyMLKEM1024) Bytes() []byte { ++ panic("cryptobackend: not available") ++} ++ ++func (ek EncapsulationKeyMLKEM1024) Encapsulate() (sharedKey, ciphertext []byte) { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsChaCha20Poly1305() bool { ++ return false ++} ++ ++func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { ++ panic("cryptobackend: not available") ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/norequirefips.go b/src/crypto/internal/backend/internal/fips140state/norequirefips.go +new file mode 100644 +index 00000000000000..1967f167d30436 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/norequirefips.go +@@ -0,0 +1,9 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !requirefips ++ ++package fips140state ++ ++const isRequireFIPS = false +diff --git a/src/crypto/internal/backend/internal/fips140state/requirefips_nosystemcrypto.go b/src/crypto/internal/backend/internal/fips140state/requirefips_nosystemcrypto.go +new file mode 100644 +index 00000000000000..4fe623e27e1d35 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/requirefips_nosystemcrypto.go +@@ -0,0 +1,15 @@ ++// Copyright 2025 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build requirefips && !goexperiment.systemcrypto ++ ++package fips140state ++ ++func init() { ++ ` ++ The requirefips tag is enabled, but no crypto backend is enabled. ++ A crypto backend is required to enable FIPS mode. ++ For more information, visit https://github.com/microsoft/go/tree/microsoft/main/eng/doc/fips ++ ` ++} +diff --git a/src/crypto/internal/backend/internal/fips140state/skipfipscheck_off.go b/src/crypto/internal/backend/internal/fips140state/skipfipscheck_off.go +new file mode 100644 +index 00000000000000..b8458bb58011d5 +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/skipfipscheck_off.go +@@ -0,0 +1,9 @@ ++// Copyright 2026 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !ms_skipfipscheck ++ ++package fips140state ++ ++const isSkipFIPSCheck = false +diff --git a/src/crypto/internal/backend/internal/fips140state/skipfipscheck_on.go b/src/crypto/internal/backend/internal/fips140state/skipfipscheck_on.go +new file mode 100644 +index 00000000000000..d13b8a7d07916e +--- /dev/null ++++ b/src/crypto/internal/backend/internal/fips140state/skipfipscheck_on.go +@@ -0,0 +1,9 @@ ++// Copyright 2026 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build ms_skipfipscheck ++ ++package fips140state ++ ++const isSkipFIPSCheck = true +diff --git a/src/crypto/internal/backend/stub.s b/src/crypto/internal/backend/stub.s +new file mode 100644 +index 00000000000000..5e4b436554d44d +--- /dev/null ++++ b/src/crypto/internal/backend/stub.s +@@ -0,0 +1,10 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++// runtime_arg0 is declared in common.go without a body. ++// It's provided by package runtime, ++// but the go command doesn't know that. ++// Having this assembly file keeps the go command ++// from complaining about the missing body ++// (because the implementation might be here). +diff --git a/src/crypto/internal/cryptotest/allocations.go b/src/crypto/internal/cryptotest/allocations.go +index 70055af70b42ec..21528faa0d6145 100644 +--- a/src/crypto/internal/cryptotest/allocations.go ++++ b/src/crypto/internal/cryptotest/allocations.go +@@ -5,7 +5,6 @@ + package cryptotest + + import ( +- "crypto/internal/boring" + "internal/asan" + "internal/msan" + "internal/race" +@@ -17,11 +16,6 @@ import ( + // SkipTestAllocations skips the test if there are any factors that interfere + // with allocation optimizations. + func SkipTestAllocations(t *testing.T) { +- // Go+BoringCrypto uses cgo. +- if boring.Enabled { +- t.Skip("skipping allocations test with BoringCrypto") +- } +- + // The sanitizers sometimes cause allocations. + if race.Enabled || msan.Enabled || asan.Enabled { + t.Skip("skipping allocations test with sanitizers") +diff --git a/src/crypto/internal/cryptotest/fips140.go b/src/crypto/internal/cryptotest/fips140.go +index a4c9fc977bc136..ef9fa5d540bd8b 100644 +--- a/src/crypto/internal/cryptotest/fips140.go ++++ b/src/crypto/internal/cryptotest/fips140.go +@@ -5,6 +5,8 @@ + package cryptotest + + import ( ++ boring "crypto/internal/backend" ++ bfips140 "crypto/internal/backend/fips140" + "crypto/internal/fips140" + "internal/testenv" + "regexp" +@@ -15,9 +17,15 @@ import ( + + func MustSupportFIPS140(tb testing.TB) { + tb.Helper() ++ if !boring.Enabled { ++ tb.Skip("skipping: boring not enabled") ++ } + if err := fips140.Supported(); err != nil { + tb.Skipf("test requires FIPS 140 mode: %v", err) + } ++ if !bfips140.Enabled() { ++ tb.Skipf("skipping: FIPS is not enabled") ++ } + } + + // MustMinimumFIPS140ModuleVersion skips the test if compiled against a lower +diff --git a/src/crypto/internal/cryptotest/hash.go b/src/crypto/internal/cryptotest/hash.go +index 37fd96a2d9d0b9..6d4b190831d57d 100644 +--- a/src/crypto/internal/cryptotest/hash.go ++++ b/src/crypto/internal/cryptotest/hash.go +@@ -5,7 +5,6 @@ + package cryptotest + + import ( +- "crypto/internal/boring" + "crypto/internal/fips140" + "hash" + "internal/testhash" +@@ -20,7 +19,7 @@ type MakeHash func() hash.Hash + // TestHash performs a set of tests on hash.Hash implementations, checking the + // documented requirements of Write, Sum, Reset, Size, and BlockSize. + func TestHash(t *testing.T, mh MakeHash) { +- if boring.Enabled || fips140.Version() == "v1.0.0" { ++ if fips140.Version() == "v1.0.0" { + testhash.TestHashWithoutClone(t, testhash.MakeHash(mh)) + return + } +diff --git a/src/crypto/internal/cryptotest/implementations.go b/src/crypto/internal/cryptotest/implementations.go +index 2b6cf4b75fc6b7..3a1b9e5cc67ecc 100644 +--- a/src/crypto/internal/cryptotest/implementations.go ++++ b/src/crypto/internal/cryptotest/implementations.go +@@ -5,7 +5,7 @@ + package cryptotest + + import ( +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/impl" + "internal/goarch" + "internal/goos" +diff --git a/src/crypto/internal/fips140hash/hash.go b/src/crypto/internal/fips140hash/hash.go +index 6d67ee8b3429a1..8f8d5937ea913c 100644 +--- a/src/crypto/internal/fips140hash/hash.go ++++ b/src/crypto/internal/fips140hash/hash.go +@@ -5,14 +5,13 @@ + package fips140hash + + import ( +- fsha3 "crypto/internal/fips140/sha3" + "crypto/sha3" + "hash" + _ "unsafe" + ) + + //go:linkname sha3Unwrap +-func sha3Unwrap(*sha3.SHA3) *fsha3.Digest ++func sha3Unwrap(*sha3.SHA3) hash.Hash + + // Unwrap returns h, or a crypto/internal/fips140 inner implementation of h. + // +diff --git a/src/crypto/internal/fips140only/fips140only.go b/src/crypto/internal/fips140only/fips140only.go +index a8d840b17022cc..2a17f7da2d4aaa 100644 +--- a/src/crypto/internal/fips140only/fips140only.go ++++ b/src/crypto/internal/fips140only/fips140only.go +@@ -18,11 +18,18 @@ func Enforced() bool { + return fips140.Enforced() + } + ++// BackendApprovedHash is set by a crypto backend during init to provide ++// backend-specific FIPS hash approval checking. If nil, only the standard ++// library FIPS hash types are recognized as approved. ++var BackendApprovedHash func(h hash.Hash) bool ++ + func ApprovedHash(h hash.Hash) bool { + switch h.(type) { + case *sha256.Digest, *sha512.Digest, *sha3.Digest: + return true +- default: +- return false + } ++ if BackendApprovedHash != nil { ++ return BackendApprovedHash(h) ++ } ++ return false + } +diff --git a/src/crypto/internal/fips140only/fips140only_test.go b/src/crypto/internal/fips140only/fips140only_test.go +index a940fb2a3a0010..878d1cb8115abb 100644 +--- a/src/crypto/internal/fips140only/fips140only_test.go ++++ b/src/crypto/internal/fips140only/fips140only_test.go +@@ -17,6 +17,7 @@ import ( + "crypto/hkdf" + "crypto/hmac" + "crypto/hpke" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "crypto/internal/fips140" + "crypto/internal/fips140only" +@@ -38,6 +39,7 @@ import ( + "io" + "math/big" + "os" ++ "runtime" + "strings" + "testing" + +@@ -81,7 +83,12 @@ func testFIPS140Only(t *testing.T) { + expectPanic(t, func() { cipher.NewCFBEncrypter(aesBlock, iv) }) + expectPanic(t, func() { cipher.NewCFBDecrypter(aesBlock, iv) }) + +- cipher.NewCTR(aesBlock, iv) ++ if boring.Enabled && (runtime.GOOS == "darwin" || runtime.GOOS == "windows") { ++ // The darwin/windows crypto backends don't support CTR. ++ expectPanic(t, func() { cipher.NewCTR(aesBlock, iv) }) ++ } else { ++ cipher.NewCTR(aesBlock, iv) ++ } + expectPanic(t, func() { cipher.NewCTR(notAESBlock, iv) }) + + expectPanic(t, func() { cipher.NewOFB(aesBlock, iv) }) +@@ -306,10 +313,12 @@ bXVL8iKLrG91IYQByUHZIn3WVAd2bfi4MfKagRt0ggd4 + expectErr(t, errRet2(hpke.Seal(hpkeK.PublicKey(), hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), nil, nil))) + expectErr(t, errRet2(hpke.Open(hpkeK, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), nil, make([]byte, 2000)))) + +- // fips140=only mode should prevent any operation that would make the FIPS +- // 140-3 module set its service indicator to false. +- if !fips140.ServiceIndicator() { +- t.Errorf("service indicator not set") ++ if !boring.Enabled { ++ // fips140=only mode should prevent any operation that would make the FIPS ++ // 140-3 module set its service indicator to false. ++ if !fips140.ServiceIndicator() { ++ t.Errorf("service indicator not set") ++ } + } + } + +@@ -322,16 +331,22 @@ type readerWrap struct { + } + + func withApprovedHash(f func(crypto.Hash)) { +- f(crypto.SHA224) +- f(crypto.SHA256) +- f(crypto.SHA384) +- f(crypto.SHA512) +- f(crypto.SHA3_224) +- f(crypto.SHA3_256) +- f(crypto.SHA3_384) +- f(crypto.SHA3_512) +- f(crypto.SHA512_224) +- f(crypto.SHA512_256) ++ for _, h := range []crypto.Hash{ ++ crypto.SHA224, crypto.SHA256, crypto.SHA384, crypto.SHA512, ++ crypto.SHA512_224, crypto.SHA512_256, ++ } { ++ if boring.Enabled && !boring.SupportsHash(h) { ++ continue ++ } ++ f(h) ++ } ++ for _, h := range []crypto.Hash{crypto.SHA3_224, crypto.SHA3_256, crypto.SHA3_384, crypto.SHA3_512} { ++ if boring.Enabled { ++ // TODO implement fallbacks that checks hash.Hash ++ continue ++ } ++ f(h) ++ } + } + + func withNonApprovedHash(f func(crypto.Hash)) { +diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go +index 90e152e1484d5d..b0d4dfc77c6225 100644 +--- a/src/crypto/internal/fips140test/acvp_test.go ++++ b/src/crypto/internal/fips140test/acvp_test.go +@@ -22,6 +22,8 @@ import ( + "bufio" + "bytes" + "crypto/elliptic" ++ boring "crypto/internal/backend" ++ bfips140 "crypto/internal/backend/fips140" + "crypto/internal/cryptotest" + entropy "crypto/internal/entropy/v1.0.0" + "crypto/internal/fips140" +@@ -2105,6 +2107,10 @@ func cmdKtsIfcResponderAft(h func() hash.Hash) command { + func TestACVP(t *testing.T) { + testenv.SkipIfShortAndSlow(t) + ++ if boring.Enabled && !bfips140.Enabled() { ++ t.Skipf("skipping: FIPS is not enabled") ++ } ++ + const ( + bsslModule = "boringssl.googlesource.com/boringssl.git" + bsslVersion = "v0.0.0-20251111011041-baaf868e6e8f" +diff --git a/src/crypto/internal/fips140test/cast_test.go b/src/crypto/internal/fips140test/cast_test.go +index 817dcb9a35a793..8efc547d0d52fc 100644 +--- a/src/crypto/internal/fips140test/cast_test.go ++++ b/src/crypto/internal/fips140test/cast_test.go +@@ -160,6 +160,7 @@ func TestConditionals(t *testing.T) { + } + + func TestCASTPasses(t *testing.T) { ++ t.Skip("Skipping because Microsoft build of Go doesn't support CAST.") + moduleStatus(t) + cryptotest.MustSupportFIPS140(t) + +@@ -183,6 +184,7 @@ func TestCASTPasses(t *testing.T) { + } + + func TestCASTFailures(t *testing.T) { ++ t.Skip("Skipping because Microsoft build of Go doesn't support CAST.") + moduleStatus(t) + cryptotest.MustSupportFIPS140(t) + +diff --git a/src/crypto/internal/fips140test/fips_test.go b/src/crypto/internal/fips140test/fips_test.go +index 7f2824ca9ac052..f0d3b2a8459871 100644 +--- a/src/crypto/internal/fips140test/fips_test.go ++++ b/src/crypto/internal/fips140test/fips_test.go +@@ -15,7 +15,7 @@ package fipstest + + import ( + "bytes" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140" + "crypto/internal/fips140/aes" + "crypto/internal/fips140/aes/gcm" +diff --git a/src/crypto/internal/rand/rand.go b/src/crypto/internal/rand/rand.go +index d12cc7586c45cc..c314c08c3186fb 100644 +--- a/src/crypto/internal/rand/rand.go ++++ b/src/crypto/internal/rand/rand.go +@@ -5,7 +5,7 @@ + package rand + + import ( +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/drbg" + "crypto/internal/randutil" + "internal/godebug" +diff --git a/src/crypto/md5/md5.go b/src/crypto/md5/md5.go +index f1287887ff5e25..61e9cc23e5667f 100644 +--- a/src/crypto/md5/md5.go ++++ b/src/crypto/md5/md5.go +@@ -12,6 +12,7 @@ package md5 + + import ( + "crypto" ++ boring "crypto/internal/backend" + "crypto/internal/fips140only" + "errors" + "hash" +@@ -114,6 +115,9 @@ func (d *digest) Clone() (hash.Cloner, error) { + // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal + // state of the hash. + func New() hash.Hash { ++ if boring.Enabled && boring.SupportsHash(crypto.MD5) && !fips140only.Enforced() { ++ return boring.NewMD5() ++ } + d := new(digest) + d.Reset() + return d +@@ -203,6 +207,12 @@ func (d *digest) checkSum() [Size]byte { + + // Sum returns the MD5 checksum of the data. + func Sum(data []byte) [Size]byte { ++ if boring.Enabled && boring.SupportsHash(crypto.MD5) { ++ if fips140only.Enforced() { ++ panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") ++ } ++ return boring.MD5(data) ++ } + var d digest + d.Reset() + d.Write(data) +diff --git a/src/crypto/md5/md5_test.go b/src/crypto/md5/md5_test.go +index 403ff2881f4b68..5417556a86700e 100644 +--- a/src/crypto/md5/md5_test.go ++++ b/src/crypto/md5/md5_test.go +@@ -6,9 +6,11 @@ package md5 + + import ( + "bytes" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "crypto/rand" + "encoding" ++ "errors" + "fmt" + "hash" + "io" +@@ -96,6 +98,9 @@ func TestGoldenMarshal(t *testing.T) { + + state, err := h.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("could not marshal: %v", err) + continue + } +@@ -183,6 +188,9 @@ func TestExtraLarge(t *testing.T) { + + // Tests that blockGeneric (pure Go) and block (in assembly for amd64, 386, arm) match. + func TestBlockGeneric(t *testing.T) { ++ if boring.Enabled { ++ t.Skip("digest is not used when boring.Enabled is set") ++ } + gen, asm := New().(*digest), New().(*digest) + buf := make([]byte, BlockSize*20) // arbitrary factor + rand.Read(buf) +@@ -230,10 +238,18 @@ func safeSum(h hash.Hash) (sum []byte, err error) { + } + + func TestLargeHashes(t *testing.T) { ++ if boring.Enabled { ++ if _, ok := New().(encoding.BinaryUnmarshaler); !ok { ++ t.Skip("BinaryUnmarshaler not implemented") ++ } ++ } + for i, test := range largeUnmarshalTests { + + h := New() + if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("test %d could not unmarshal: %v", i, err) + continue + } +@@ -271,7 +287,7 @@ func TestMD5Hash(t *testing.T) { + + func TestExtraMethods(t *testing.T) { + h := maybeCloner(New()) +- cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + } + + func maybeCloner(h hash.Hash) any { +diff --git a/src/crypto/mlkem/mlkem.go b/src/crypto/mlkem/mlkem.go +index f0fd6a4993a8e6..53600fdf265062 100644 +--- a/src/crypto/mlkem/mlkem.go ++++ b/src/crypto/mlkem/mlkem.go +@@ -13,7 +13,9 @@ package mlkem + + import ( + "crypto" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/mlkem" ++ "crypto/internal/rand" + ) + + const ( +@@ -36,38 +38,66 @@ const ( + EncapsulationKeySize1024 = 1568 + ) + +-// DecapsulationKey768 is the secret key used to decapsulate a shared key ++// A DecapsulationKey768 is the secret key used to decapsulate a shared key + // from a ciphertext. It includes various precomputed values. + type DecapsulationKey768 struct { +- key *mlkem.DecapsulationKey768 ++ key *mlkem.DecapsulationKey768 ++ boringKey boring.DecapsulationKeyMLKEM768 ++} ++ ++func supportsBoringMLKEM768() bool { ++ return boring.Enabled && rand.IsDefaultReader(rand.Reader) && boring.SupportsMLKEM768() ++} ++ ++func supportsBoringMLKEM1024() bool { ++ return boring.Enabled && rand.IsDefaultReader(rand.Reader) && boring.SupportsMLKEM1024() + } + + // GenerateKey768 generates a new decapsulation key, drawing random bytes from + // a secure source. The decapsulation key must be kept secret. + func GenerateKey768() (*DecapsulationKey768, error) { ++ if supportsBoringMLKEM768() { ++ key, err := boring.GenerateKeyMLKEM768() ++ if err != nil { ++ return nil, err ++ } ++ return &DecapsulationKey768{boringKey: key}, nil ++ } ++ + key, err := mlkem.GenerateKey768() + if err != nil { + return nil, err + } + +- return &DecapsulationKey768{key}, nil ++ return &DecapsulationKey768{key: key}, nil + } + + // NewDecapsulationKey768 expands a decapsulation key from a 64-byte seed in the + // "d || z" form. The seed must be uniformly random. + func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) { ++ if supportsBoringMLKEM768() { ++ key, err := boring.NewDecapsulationKeyMLKEM768(seed) ++ if err != nil { ++ return nil, err ++ } ++ return &DecapsulationKey768{boringKey: key}, nil ++ } ++ + key, err := mlkem.NewDecapsulationKey768(seed) + if err != nil { + return nil, err + } + +- return &DecapsulationKey768{key}, nil ++ return &DecapsulationKey768{key: key}, nil + } + + // Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. + // + // The decapsulation key must be kept secret. + func (dk *DecapsulationKey768) Bytes() []byte { ++ if dk.key == nil { ++ return dk.boringKey.Bytes() ++ } + return dk.key.Bytes() + } + +@@ -79,13 +109,19 @@ func (dk *DecapsulationKey768) Bytes() []byte { + // + // The shared key must be kept secret. + func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { ++ if dk.key == nil { ++ return dk.boringKey.Decapsulate(ciphertext) ++ } + return dk.key.Decapsulate(ciphertext) + } + + // EncapsulationKey returns the public encapsulation key necessary to produce + // ciphertexts. + func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { +- return &EncapsulationKey768{dk.key.EncapsulationKey()} ++ if dk.key == nil { ++ return &EncapsulationKey768{boringKey: dk.boringKey.EncapsulationKey()} ++ } ++ return &EncapsulationKey768{key: dk.key.EncapsulationKey()} + } + + // Encapsulator returns the encapsulation key, like +@@ -101,22 +137,34 @@ var _ crypto.Decapsulator = (*DecapsulationKey768)(nil) + // An EncapsulationKey768 is the public key used to produce ciphertexts to be + // decapsulated by the corresponding DecapsulationKey768. + type EncapsulationKey768 struct { +- key *mlkem.EncapsulationKey768 ++ key *mlkem.EncapsulationKey768 ++ boringKey boring.EncapsulationKeyMLKEM768 + } + + // NewEncapsulationKey768 parses an encapsulation key from its encoded form. If + // the encapsulation key is not valid, NewEncapsulationKey768 returns an error. + func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) { ++ if supportsBoringMLKEM768() { ++ key, err := boring.NewEncapsulationKeyMLKEM768(encapsulationKey) ++ if err != nil { ++ return nil, err ++ } ++ return &EncapsulationKey768{boringKey: key}, nil ++ } ++ + key, err := mlkem.NewEncapsulationKey768(encapsulationKey) + if err != nil { + return nil, err + } + +- return &EncapsulationKey768{key}, nil ++ return &EncapsulationKey768{key: key}, nil + } + + // Bytes returns the encapsulation key as a byte slice. + func (ek *EncapsulationKey768) Bytes() []byte { ++ if ek.key == nil { ++ return ek.boringKey.Bytes() ++ } + return ek.key.Bytes() + } + +@@ -128,41 +176,64 @@ func (ek *EncapsulationKey768) Bytes() []byte { + // For testing, derandomized encapsulation is provided by the + // [crypto/mlkem/mlkemtest] package. + func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) { ++ if ek.key == nil { ++ return ek.boringKey.Encapsulate() ++ } + return ek.key.Encapsulate() + } + + // DecapsulationKey1024 is the secret key used to decapsulate a shared key + // from a ciphertext. It includes various precomputed values. + type DecapsulationKey1024 struct { +- key *mlkem.DecapsulationKey1024 ++ key *mlkem.DecapsulationKey1024 ++ boringKey boring.DecapsulationKeyMLKEM1024 + } + + // GenerateKey1024 generates a new decapsulation key, drawing random bytes from + // a secure source. The decapsulation key must be kept secret. + func GenerateKey1024() (*DecapsulationKey1024, error) { ++ if supportsBoringMLKEM1024() { ++ key, err := boring.GenerateKeyMLKEM1024() ++ if err != nil { ++ return nil, err ++ } ++ return &DecapsulationKey1024{boringKey: key}, nil ++ } ++ + key, err := mlkem.GenerateKey1024() + if err != nil { + return nil, err + } + +- return &DecapsulationKey1024{key}, nil ++ return &DecapsulationKey1024{key: key}, nil + } + + // NewDecapsulationKey1024 expands a decapsulation key from a 64-byte seed in the + // "d || z" form. The seed must be uniformly random. + func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) { ++ if supportsBoringMLKEM1024() { ++ key, err := boring.NewDecapsulationKeyMLKEM1024(seed) ++ if err != nil { ++ return nil, err ++ } ++ return &DecapsulationKey1024{boringKey: key}, nil ++ } ++ + key, err := mlkem.NewDecapsulationKey1024(seed) + if err != nil { + return nil, err + } + +- return &DecapsulationKey1024{key}, nil ++ return &DecapsulationKey1024{key: key}, nil + } + + // Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. + // + // The decapsulation key must be kept secret. + func (dk *DecapsulationKey1024) Bytes() []byte { ++ if dk.key == nil { ++ return dk.boringKey.Bytes() ++ } + return dk.key.Bytes() + } + +@@ -174,13 +245,19 @@ func (dk *DecapsulationKey1024) Bytes() []byte { + // + // The shared key must be kept secret. + func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { ++ if dk.key == nil { ++ return dk.boringKey.Decapsulate(ciphertext) ++ } + return dk.key.Decapsulate(ciphertext) + } + + // EncapsulationKey returns the public encapsulation key necessary to produce + // ciphertexts. + func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { +- return &EncapsulationKey1024{dk.key.EncapsulationKey()} ++ if dk.key == nil { ++ return &EncapsulationKey1024{boringKey: dk.boringKey.EncapsulationKey()} ++ } ++ return &EncapsulationKey1024{key: dk.key.EncapsulationKey()} + } + + // Encapsulator returns the encapsulation key, like +@@ -196,22 +273,34 @@ var _ crypto.Decapsulator = (*DecapsulationKey1024)(nil) + // An EncapsulationKey1024 is the public key used to produce ciphertexts to be + // decapsulated by the corresponding DecapsulationKey1024. + type EncapsulationKey1024 struct { +- key *mlkem.EncapsulationKey1024 ++ key *mlkem.EncapsulationKey1024 ++ boringKey boring.EncapsulationKeyMLKEM1024 + } + + // NewEncapsulationKey1024 parses an encapsulation key from its encoded form. If + // the encapsulation key is not valid, NewEncapsulationKey1024 returns an error. + func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) { ++ if supportsBoringMLKEM1024() { ++ key, err := boring.NewEncapsulationKeyMLKEM1024(encapsulationKey) ++ if err != nil { ++ return nil, err ++ } ++ return &EncapsulationKey1024{boringKey: key}, nil ++ } ++ + key, err := mlkem.NewEncapsulationKey1024(encapsulationKey) + if err != nil { + return nil, err + } + +- return &EncapsulationKey1024{key}, nil ++ return &EncapsulationKey1024{key: key}, nil + } + + // Bytes returns the encapsulation key as a byte slice. + func (ek *EncapsulationKey1024) Bytes() []byte { ++ if ek.key == nil { ++ return ek.boringKey.Bytes() ++ } + return ek.key.Bytes() + } + +@@ -223,5 +312,8 @@ func (ek *EncapsulationKey1024) Bytes() []byte { + // For testing, derandomized encapsulation is provided by the + // [crypto/mlkem/mlkemtest] package. + func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) { ++ if ek.key == nil { ++ return ek.boringKey.Encapsulate() ++ } + return ek.key.Encapsulate() + } +diff --git a/src/crypto/mlkem/mlkem_test.go b/src/crypto/mlkem/mlkem_test.go +index e1c2ef49f15ce0..23504c64f4d805 100644 +--- a/src/crypto/mlkem/mlkem_test.go ++++ b/src/crypto/mlkem/mlkem_test.go +@@ -6,6 +6,7 @@ package mlkem_test + + import ( + "bytes" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/mlkem" + "crypto/internal/fips140/sha3" + . "crypto/mlkem" +@@ -164,6 +165,10 @@ var millionFlag = flag.Bool("million", false, "run the million vector test") + // TestAccumulated accumulates 10k (or 100, or 1M) random vectors and checks the + // hash of the result, to avoid checking in 150MB of test vectors. + func TestAccumulated(t *testing.T) { ++ if boring.Enabled { ++ t.Skip("skipping TestAccumulated with backend enabled (EncapsulateInternal not available)") ++ } ++ + n := 10000 + expected := "8a518cc63da366322a8e7a818c7a0d63483cb3528d34a4cf42f35d5ad73f22fc" + if testing.Short() { +@@ -234,6 +239,9 @@ func BenchmarkKeyGen(b *testing.B) { + } + + func BenchmarkEncaps(b *testing.B) { ++ if boring.Enabled { ++ b.Skip("skipping BenchmarkEncaps with backend enabled (EncapsulateInternal not available)") ++ } + seed := make([]byte, SeedSize) + rand.Read(seed) + dk, err := NewDecapsulationKey768(seed) +diff --git a/src/crypto/pbkdf2/pbkdf2.go b/src/crypto/pbkdf2/pbkdf2.go +index 0bc14be888d9d6..4172402e1bb620 100644 +--- a/src/crypto/pbkdf2/pbkdf2.go ++++ b/src/crypto/pbkdf2/pbkdf2.go +@@ -11,6 +11,7 @@ + package pbkdf2 + + import ( ++ boring "crypto/internal/backend" + "crypto/internal/fips140/pbkdf2" + "crypto/internal/fips140hash" + "crypto/internal/fips140only" +@@ -50,5 +51,11 @@ func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyL + return nil, errors.New("crypto/pbkdf2: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + } ++ if boring.Enabled && boring.SupportsPBKDF2() { ++ if keyLength <= 0 { ++ return nil, errors.New("pkbdf2: keyLength must be larger than 0") ++ } ++ return boring.PBKDF2([]byte(password), salt, iter, keyLength, fh) ++ } + return pbkdf2.Key(fh, password, salt, iter, keyLength) + } +diff --git a/src/crypto/pbkdf2/pbkdf2_test.go b/src/crypto/pbkdf2/pbkdf2_test.go +index eb0ed14e243c6b..5023f354ba7fe3 100644 +--- a/src/crypto/pbkdf2/pbkdf2_test.go ++++ b/src/crypto/pbkdf2/pbkdf2_test.go +@@ -6,12 +6,13 @@ package pbkdf2_test + + import ( + "bytes" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140" + "crypto/pbkdf2" + "crypto/sha1" + "crypto/sha256" + "hash" ++ "internal/systemcrypto" + "testing" + ) + +@@ -223,6 +224,9 @@ func TestPBKDF2ServiceIndicator(t *testing.T) { + } + + func TestMaxKeyLength(t *testing.T) { ++ if systemcrypto.Enabled() { ++ t.Skip("Some backends don't support keys longer than 32 bytes") ++ } + // This error cannot be triggered on platforms where int is 31 bits (i.e. + // 32-bit platforms), since the max value for keyLength is 1<<31-1 and + // 1<<31-1 * hLen will always be less than 1<<32-1 * hLen. +diff --git a/src/crypto/purego_test.go b/src/crypto/purego_test.go +index ebc9110cecee82..aa4aec046795cf 100644 +--- a/src/crypto/purego_test.go ++++ b/src/crypto/purego_test.go +@@ -41,7 +41,7 @@ func TestPureGoTag(t *testing.T) { + } + + for _, pkgName := range pkgs { +- if strings.Contains(pkgName, "/boring") { ++ if strings.Contains(pkgName, "/boring") || strings.Contains(pkgName, "/internal/backend") || strings.Contains(pkgName, "tls/fipsonly") { + continue + } + +diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go +index 018fe013cef600..ec710c6f935e3b 100644 +--- a/src/crypto/rand/rand.go ++++ b/src/crypto/rand/rand.go +@@ -7,7 +7,7 @@ + package rand + + import ( +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/drbg" + "crypto/internal/rand" + "io" +diff --git a/src/crypto/rand/rand_test.go b/src/crypto/rand/rand_test.go +index 596f6d434065fd..466de618fe2bc5 100644 +--- a/src/crypto/rand/rand_test.go ++++ b/src/crypto/rand/rand_test.go +@@ -7,6 +7,7 @@ package rand + import ( + "bytes" + "compress/flate" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "crypto/internal/rand" + "errors" +@@ -157,13 +158,17 @@ var sink byte + + func TestAllocations(t *testing.T) { + cryptotest.SkipTestAllocations(t) ++ expectedAllocations := 0 ++ if boring.Enabled { ++ expectedAllocations = 1 ++ } + n := int(testing.AllocsPerRun(10, func() { + buf := make([]byte, 32) + Read(buf) + sink ^= buf[0] + })) +- if n > 0 { +- t.Errorf("allocs = %d, want 0", n) ++ if n > expectedAllocations { ++ t.Errorf("allocs = %d, want %d", n, expectedAllocations) + } + } + +diff --git a/src/crypto/rc4/rc4.go b/src/crypto/rc4/rc4.go +index c4d2b0d382e7fc..ba8dc141117e59 100644 +--- a/src/crypto/rc4/rc4.go ++++ b/src/crypto/rc4/rc4.go +@@ -10,6 +10,7 @@ + package rc4 + + import ( ++ boring "crypto/internal/backend" + "crypto/internal/fips140/alias" + "crypto/internal/fips140only" + "errors" +@@ -20,6 +21,8 @@ import ( + type Cipher struct { + s [256]uint32 + i, j uint8 ++ ++ boring *boring.RC4Cipher + } + + type KeySizeError int +@@ -38,6 +41,13 @@ func NewCipher(key []byte) (*Cipher, error) { + if k < 1 || k > 256 { + return nil, KeySizeError(k) + } ++ if boring.Enabled && boring.SupportsRC4() { ++ c, err := boring.NewRC4Cipher(key) ++ if err != nil { ++ return nil, err ++ } ++ return &Cipher{boring: c}, nil ++ } + var c Cipher + for i := 0; i < 256; i++ { + c.s[i] = uint32(i) +@@ -55,6 +65,10 @@ func NewCipher(key []byte) (*Cipher, error) { + // Deprecated: Reset can't guarantee that the key will be entirely removed from + // the process's memory. + func (c *Cipher) Reset() { ++ if boring.Enabled && boring.SupportsRC4() { ++ c.boring.Reset() ++ return ++ } + clear(c.s[:]) + c.i, c.j = 0, 0 + } +@@ -62,6 +76,10 @@ func (c *Cipher) Reset() { + // XORKeyStream sets dst to the result of XORing src with the key stream. + // Dst and src must overlap entirely or not at all. + func (c *Cipher) XORKeyStream(dst, src []byte) { ++ if boring.Enabled && boring.SupportsRC4() { ++ c.boring.XORKeyStream(dst, src) ++ return ++ } + if len(src) == 0 { + return + } +diff --git a/src/crypto/rsa/boring.go b/src/crypto/rsa/boring.go +index b9f9d3154f2589..92b960f7219945 100644 +--- a/src/crypto/rsa/boring.go ++++ b/src/crypto/rsa/boring.go +@@ -2,15 +2,16 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + package rsa + + import ( +- "crypto/internal/boring" +- "crypto/internal/boring/bbig" ++ boring "crypto/internal/backend" ++ "crypto/internal/backend/bbig" + "crypto/internal/boring/bcache" + "math/big" ++ "runtime" + ) + + // Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. +@@ -62,6 +63,11 @@ type boringPriv struct { + } + + func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyRSA, error) { ++ // CommonCrypto requires the CRT values to be precomputed if nil ++ if runtime.GOOS == "darwin" && (priv.Precomputed.Dp == nil || priv.Precomputed.Dq == nil || priv.Precomputed.Qinv == nil) { ++ priv.Precompute() ++ priv.Precomputed.fips = nil ++ } + b := privCache.Get(priv) + if b != nil && privateKeyEqual(&b.orig, priv) { + return b.key, nil +diff --git a/src/crypto/rsa/boring_test.go b/src/crypto/rsa/boring_test.go +index 838fcc1244bdbe..d89f732345e8a3 100644 +--- a/src/crypto/rsa/boring_test.go ++++ b/src/crypto/rsa/boring_test.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + // Note: Can run these tests against the non-BoringCrypto + // version of the code by using "CGO_ENABLED=0 go test". +diff --git a/src/crypto/rsa/fips.go b/src/crypto/rsa/fips.go +index e756512fecd6d5..9b35eab6e3a811 100644 +--- a/src/crypto/rsa/fips.go ++++ b/src/crypto/rsa/fips.go +@@ -6,7 +6,7 @@ package rsa + + import ( + "crypto" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/rsa" + "crypto/internal/fips140hash" + "crypto/internal/fips140only" +@@ -71,17 +71,6 @@ func SignPSS(random io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte + hash = opts.Hash + } + +- if boring.Enabled && rand.IsDefaultReader(random) && priv.N.BitLen() >= 1024 { +- bkey, err := boringPrivateKey(priv) +- if err != nil { +- return nil, err +- } +- return boring.SignRSAPSS(bkey, hash, digest, opts.saltLength()) +- } +- if priv.N.BitLen() >= 1024 { +- boring.UnreachableExceptTests() +- } +- + if !hash.Available() { + return nil, errors.New("crypto/rsa: requested hash function unavailable: " + hash.String()) + } +@@ -97,6 +86,14 @@ func SignPSS(random io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } + ++ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSASaltLength(true, opts.saltLength()) && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsHash(hash) { ++ bkey, err := boringPrivateKey(priv) ++ if err != nil { ++ return nil, err ++ } ++ return boring.SignRSAPSS(bkey, hash, digest, opts.saltLength()) ++ } ++ + k, err := fipsPrivateKey(priv) + if err != nil { + return nil, err +@@ -139,17 +136,6 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts + return err + } + +- if boring.Enabled { +- bkey, err := boringPublicKey(pub) +- if err != nil { +- return err +- } +- if err := boring.VerifyRSAPSS(bkey, hash, digest, sig, opts.saltLength()); err != nil { +- return ErrVerification +- } +- return nil +- } +- + if !hash.Available() { + return errors.New("crypto/rsa: requested hash function unavailable: " + hash.String()) + } +@@ -162,6 +148,17 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts + return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + ++ if boring.Enabled && boring.SupportsRSASaltLength(false, opts.saltLength()) && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsHash(hash) { ++ bkey, err := boringPublicKey(pub) ++ if err != nil { ++ return err ++ } ++ if err := boring.VerifyRSAPSS(bkey, hash, digest, sig, opts.saltLength()); err != nil { ++ return ErrVerification ++ } ++ return nil ++ } ++ + k, err := fipsPublicKey(pub) + if err != nil { + return err +@@ -217,10 +214,12 @@ func EncryptOAEPWithOptions(random io.Reader, pub *PublicKey, msg []byte, opts * + if opts.MGFHash != 0 && !opts.MGFHash.Available() { + return nil, errors.New("crypto/rsa: requested hash function unavailable: " + opts.MGFHash.String()) + } +- if opts.MGFHash == 0 { +- return encryptOAEP(opts.Hash.New(), opts.Hash.New(), random, pub, msg, opts.Label) ++ hash := opts.Hash.New() ++ mgfhash := hash ++ if opts.MGFHash != 0 && opts.Hash != opts.MGFHash { ++ mgfhash = opts.MGFHash.New() + } +- return encryptOAEP(opts.Hash.New(), opts.MGFHash.New(), random, pub, msg, opts.Label) ++ return encryptOAEP(hash, mgfhash, random, pub, msg, opts.Label) + } + + func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) { +@@ -231,7 +230,7 @@ func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *Publi + defer hash.Reset() + defer mgfHash.Reset() + +- if boring.Enabled && rand.IsDefaultReader(random) { ++ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAOAEPLabel(label) && hash == mgfHash { + k := pub.Size() + if len(msg) > k-2*hash.Size()-2 { + return nil, ErrMessageTooLong +@@ -242,7 +241,6 @@ func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *Publi + } + return boring.EncryptRSAOAEP(hash, mgfHash, bkey, msg, label) + } +- boring.UnreachableExceptTests() + + hash = fips140hash.Unwrap(hash) + +@@ -283,7 +281,7 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l + return nil, err + } + +- if boring.Enabled && priv.N.BitLen() >= 1024 { ++ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAOAEPLabel(label) && hash == mgfHash { + k := priv.Size() + if len(ciphertext) > k || + k < hash.Size()*2+2 { +@@ -345,23 +343,23 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ + return nil, err + } + +- if boring.Enabled && priv.N.BitLen() >= 1024 { +- bkey, err := boringPrivateKey(priv) +- if err != nil { +- return nil, err +- } +- return boring.SignRSAPKCS1v15(bkey, hash, hashed) +- } +- + if err := checkFIPS140OnlyPrivateKey(priv); err != nil { + return nil, err + } + if fips140only.Enforced() { +- if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { ++ if hash == crypto.Hash(0) || !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + } + ++ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAPKCS1v15Signature(hash) { ++ bkey, err := boringPrivateKey(priv) ++ if err != nil { ++ return nil, err ++ } ++ return boring.SignRSAPKCS1v15(bkey, hash, hashed) ++ } ++ + k, err := fipsPrivateKey(priv) + if err != nil { + return nil, err +@@ -390,7 +388,16 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) + return err + } + +- if boring.Enabled { ++ if err := checkFIPS140OnlyPublicKey(pub); err != nil { ++ return err ++ } ++ if fips140only.Enforced() { ++ if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { ++ return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") ++ } ++ } ++ ++ if boring.Enabled && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAPKCS1v15Signature(hash) { + bkey, err := boringPublicKey(pub) + if err != nil { + return err +@@ -401,15 +408,6 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) + return nil + } + +- if err := checkFIPS140OnlyPublicKey(pub); err != nil { +- return err +- } +- if fips140only.Enforced() { +- if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { +- return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") +- } +- } +- + k, err := fipsPublicKey(pub) + if err != nil { + return err +diff --git a/src/crypto/rsa/notboring.go b/src/crypto/rsa/notboring.go +index 2abc0436405f8a..3e4d6f3eef61e6 100644 +--- a/src/crypto/rsa/notboring.go ++++ b/src/crypto/rsa/notboring.go +@@ -2,11 +2,11 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build !boringcrypto ++//go:build !goexperiment.systemcrypto + + package rsa + +-import "crypto/internal/boring" ++import boring "crypto/internal/backend" + + func boringPublicKey(*PublicKey) (*boring.PublicKeyRSA, error) { + panic("boringcrypto: not available") +diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go +index bfe7346fc67c54..003030ec762ebf 100644 +--- a/src/crypto/rsa/pkcs1v15.go ++++ b/src/crypto/rsa/pkcs1v15.go +@@ -5,7 +5,7 @@ + package rsa + + import ( +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/rsa" + "crypto/internal/fips140only" + "crypto/internal/rand" +@@ -61,7 +61,7 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro + return nil, ErrMessageTooLong + } + +- if boring.Enabled && rand.IsDefaultReader(random) { ++ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAPKCS1v15Encryption() { + bkey, err := boringPublicKey(pub) + if err != nil { + return nil, err +@@ -83,7 +83,7 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro + em[len(em)-len(msg)-1] = 0 + copy(mm, msg) + +- if boring.Enabled { ++ if boring.Enabled && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAPKCS1v15Encryption() { + var bkey *boring.PublicKeyRSA + bkey, err = boringPublicKey(pub) + if err != nil { +@@ -115,7 +115,7 @@ func DecryptPKCS1v15(random io.Reader, priv *PrivateKey, ciphertext []byte) ([]b + return nil, err + } + +- if boring.Enabled && priv.N.BitLen() >= 1024 { ++ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAPKCS1v15Encryption() { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err +@@ -221,7 +221,7 @@ func decryptPKCS1v15(priv *PrivateKey, ciphertext []byte) (valid int, em []byte, + return 0, nil, 0, err + } + +- if boring.Enabled && priv.N.BitLen() >= 1024 { ++ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAPKCS1v15Encryption() { + var bkey *boring.PrivateKeyRSA + bkey, err = boringPrivateKey(priv) + if err != nil { +diff --git a/src/crypto/rsa/pkcs1v15_test.go b/src/crypto/rsa/pkcs1v15_test.go +index c65552cd93526a..910416abe842f5 100644 +--- a/src/crypto/rsa/pkcs1v15_test.go ++++ b/src/crypto/rsa/pkcs1v15_test.go +@@ -7,6 +7,7 @@ package rsa_test + import ( + "bytes" + "crypto" ++ boring "crypto/internal/backend" + "crypto/rand" + . "crypto/rsa" + "crypto/sha1" +@@ -235,6 +236,10 @@ func TestVerifyPKCS1v15(t *testing.T) { + } + + func TestOverlongMessagePKCS1v15(t *testing.T) { ++ if boring.Enabled { ++ // See for example https://github.com/golang-fips/openssl/pull/142. ++ t.Skip("Some backends do not return an error for overlong messages.") ++ } + t.Setenv("GODEBUG", "rsa1024min=0") + ciphertext := decodeBase64("fjOVdirUzFoLlukv80dBllMLjXythIf22feqPrNo0YoIjzyzyoMFiLjAc/Y4krkeZ11XFThIrEvw\nkRiZcCq5ng==") + _, err := DecryptPKCS1v15(nil, test512Key, ciphertext) +diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go +index e03f4ab06603c6..088f87ee6a536e 100644 +--- a/src/crypto/rsa/pss_test.go ++++ b/src/crypto/rsa/pss_test.go +@@ -8,14 +8,17 @@ import ( + "bufio" + "compress/bzip2" + "crypto" ++ boring "crypto/internal/backend" + "crypto/internal/fips140" + "crypto/rand" + . "crypto/rsa" + "crypto/sha256" + "crypto/sha512" + "encoding/hex" ++ "internal/systemcrypto" + "math/big" + "os" ++ "runtime" + "strconv" + "strings" + "testing" +@@ -104,6 +107,9 @@ func TestPSSGolden(t *testing.T) { + hashed = h.Sum(hashed[:0]) + + if err := VerifyPSS(key, hash, hashed, sig, opts); err != nil { ++ if systemcrypto.Enabled() && runtime.GOOS == "darwin" && key.N.BitLen() == 1025 { ++ t.Skip("CommonCrypto doesn't support golden test entries with this key size") ++ } + t.Error(err) + } + default: +@@ -180,6 +186,10 @@ func TestPSSSigning(t *testing.T) { + continue + } + ++ if boring.Enabled && test.good != test.fipsGood { ++ t.Skip("skipping test with different results when using different backends") ++ } ++ + opts.SaltLength = test.verifySaltLength + err = VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, &opts) + good := test.good +@@ -241,7 +251,9 @@ func fromHex(hexStr string) []byte { + + func TestInvalidPSSSaltLength(t *testing.T) { + t.Setenv("GODEBUG", "rsa1024min=0") +- key, err := GenerateKey(rand.Reader, 245) ++ // Most crypto backends don't support generating RSA keys with a bit size ++ // lower than 512, so we use 512 here. ++ key, err := GenerateKey(rand.Reader, 512) + if err != nil { + t.Fatal(err) + } +diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go +index 93e08ed6cd8f71..2e8392c7ac34cf 100644 +--- a/src/crypto/rsa/rsa.go ++++ b/src/crypto/rsa/rsa.go +@@ -43,8 +43,8 @@ package rsa + + import ( + "crypto" +- "crypto/internal/boring" +- "crypto/internal/boring/bbig" ++ boring "crypto/internal/backend" ++ "crypto/internal/backend/bbig" + "crypto/internal/fips140/bigmod" + "crypto/internal/fips140/rsa" + "crypto/internal/fips140only" +@@ -182,11 +182,12 @@ func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.D + if opts.MGFHash != 0 && !opts.MGFHash.Available() { + return nil, errors.New("rsa: requested hash function unavailable: " + opts.MGFHash.String()) + } +- if opts.MGFHash == 0 { +- return decryptOAEP(opts.Hash.New(), opts.Hash.New(), priv, ciphertext, opts.Label) +- } else { +- return decryptOAEP(opts.Hash.New(), opts.MGFHash.New(), priv, ciphertext, opts.Label) ++ hash := opts.Hash.New() ++ mgfhash := hash ++ if opts.MGFHash != 0 && opts.Hash != opts.MGFHash { ++ mgfhash = opts.MGFHash.New() + } ++ return decryptOAEP(hash, mgfhash, priv, ciphertext, opts.Label) + + case *PKCS1v15DecryptOptions: + if l := opts.SessionKeyLen; l > 0 { +@@ -320,8 +321,14 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { + return nil, err + } + +- if boring.Enabled && rand.IsDefaultReader(random) && +- (bits == 2048 || bits == 3072 || bits == 4096) { ++ if fips140only.Enforced() && bits < 2048 { ++ return nil, errors.New("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode") ++ } ++ if fips140only.Enforced() && bits%2 == 1 { ++ return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode") ++ } ++ ++ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSAPublicKey(bits) { + bN, bE, bD, bP, bQ, bDp, bDq, bQinv, err := boring.GenerateKeyRSA(bits) + if err != nil { + return nil, err +@@ -358,12 +365,6 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { + + random = rand.CustomReader(random) + +- if fips140only.Enforced() && bits < 2048 { +- return nil, errors.New("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode") +- } +- if fips140only.Enforced() && bits%2 == 1 { +- return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode") +- } + if fips140only.Enforced() && !fips140only.ApprovedRandomReader(random) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } +diff --git a/src/crypto/rsa/rsa_darwin.go b/src/crypto/rsa/rsa_darwin.go +new file mode 100644 +index 00000000000000..b549c6e3b17484 +--- /dev/null ++++ b/src/crypto/rsa/rsa_darwin.go +@@ -0,0 +1,71 @@ ++// Copyright 2017 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package rsa ++ ++import ( ++ "crypto/internal/backend" ++ "crypto/internal/backend/bbig" ++ "errors" ++ "math/big" ++ _ "unsafe" ++ ++ "golang.org/x/crypto/cryptobyte" ++ "golang.org/x/crypto/cryptobyte/asn1" ++) ++ ++//go:linkname decodeKey ++func decodeKey(data []byte) (N, E, D, P, Q, Dp, Dq, Qinv backend.BigInt, err error) { ++ bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv backend.BigInt, err error) { ++ return nil, nil, nil, nil, nil, nil, nil, nil, e ++ } ++ input := cryptobyte.String(data) ++ var version int ++ n, e, d, p, q, dp, dq, qinv := new(big.Int), new(big.Int), new(big.Int), new(big.Int), ++ new(big.Int), new(big.Int), new(big.Int), new(big.Int) ++ // Parse the ASN.1 sequence ++ if !input.ReadASN1(&input, asn1.SEQUENCE) { ++ return bad(errors.New("invalid ASN.1 structure: not a sequence")) ++ } ++ if !input.ReadASN1Integer(&version) || version != 0 { ++ return bad(errors.New("invalid ASN.1 structure: unsupported version")) ++ } ++ if !input.ReadASN1Integer(n) || !input.ReadASN1Integer(e) || ++ !input.ReadASN1Integer(d) || !input.ReadASN1Integer(p) || ++ !input.ReadASN1Integer(q) || !input.ReadASN1Integer(dp) || ++ !input.ReadASN1Integer(dq) || !input.ReadASN1Integer(qinv) { ++ return bad(errors.New("invalid ASN.1 structure")) ++ } ++ return bbig.Enc(n), bbig.Enc(e), bbig.Enc(d), bbig.Enc(p), bbig.Enc(q), ++ bbig.Enc(dp), bbig.Enc(dq), bbig.Enc(qinv), nil ++} ++ ++//go:linkname encodeKey ++func encodeKey(N, E, D, P, Q, Dp, Dq, Qinv backend.BigInt) ([]byte, error) { ++ builder := cryptobyte.NewBuilder(nil) ++ builder.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { ++ b.AddASN1Int64(0) // Add version as int64 ++ b.AddASN1BigInt(bbig.Dec(N)) // Add modulus ++ b.AddASN1BigInt(bbig.Dec(E)) // Add public exponent ++ b.AddASN1BigInt(bbig.Dec(D)) // Add private exponent ++ b.AddASN1BigInt(bbig.Dec(P)) // Add prime1 ++ b.AddASN1BigInt(bbig.Dec(Q)) // Add prime2 ++ b.AddASN1BigInt(bbig.Dec(Dp)) // Add exponent1 ++ b.AddASN1BigInt(bbig.Dec(Dq)) // Add exponent2 ++ b.AddASN1BigInt(bbig.Dec(Qinv)) // Add coefficient ++ }) ++ return builder.Bytes() ++} ++ ++//go:linkname encodePublicKey ++func encodePublicKey(N, E backend.BigInt) ([]byte, error) { ++ builder := cryptobyte.NewBuilder(nil) ++ builder.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { ++ b.AddASN1BigInt(bbig.Dec(N)) // Add modulus ++ b.AddASN1BigInt(bbig.Dec(E)) // Add public exponent ++ }) ++ return builder.Bytes() ++} +diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go +index 8c1b22bf4f41b4..ca8a3740e8d31a 100644 +--- a/src/crypto/rsa/rsa_test.go ++++ b/src/crypto/rsa/rsa_test.go +@@ -8,7 +8,7 @@ import ( + "bufio" + "bytes" + "crypto" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "crypto/internal/fips140" + "crypto/internal/fips140/ecdsa" +@@ -228,6 +228,11 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) { + if priv.D.Cmp(priv.N) > 0 { + t.Errorf("private exponent too large") + } ++ if boring.Enabled && priv.N.BitLen() < 512 { ++ // Some crypto backends (e.g. CNG and OpenSSL with SymCrypt) don't support key sizes ++ // lower than 512 and intentionally fail rather than fall back to Go crypto. ++ t.Skip("skipping allocations test with BoringCrypto") ++ } + + msg := []byte("hi!") + enc, err := EncryptPKCS1v15(rand.Reader, &priv.PublicKey, msg) +diff --git a/src/crypto/sha1/sha1.go b/src/crypto/sha1/sha1.go +index 46e47df1d32cf2..05a1368f9833e5 100644 +--- a/src/crypto/sha1/sha1.go ++++ b/src/crypto/sha1/sha1.go +@@ -10,7 +10,7 @@ package sha1 + + import ( + "crypto" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140only" + "errors" + "hash" +@@ -113,7 +113,7 @@ func (d *digest) Reset() { + // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal + // state of the hash. + func New() hash.Hash { +- if boring.Enabled { ++ if boring.Enabled && !fips140only.Enforced() { + return boring.NewSHA1() + } + d := new(digest) +@@ -153,7 +153,6 @@ func (d *digest) Write(p []byte) (nn int, err error) { + } + + func (d *digest) Sum(in []byte) []byte { +- boring.Unreachable() + // Make a copy of d so that caller can keep writing and summing. + d0 := *d + hash := d0.checkSum() +@@ -271,12 +270,12 @@ func (d *digest) constSum() [Size]byte { + + // Sum returns the SHA-1 checksum of the data. + func Sum(data []byte) [Size]byte { +- if boring.Enabled { +- return boring.SHA1(data) +- } + if fips140only.Enforced() { + panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") + } ++ if boring.Enabled { ++ return boring.SHA1(data) ++ } + var d digest + d.Reset() + d.Write(data) +diff --git a/src/crypto/sha1/sha1_test.go b/src/crypto/sha1/sha1_test.go +index ef6e5ddcbb2d97..cc88965f966aae 100644 +--- a/src/crypto/sha1/sha1_test.go ++++ b/src/crypto/sha1/sha1_test.go +@@ -8,9 +8,10 @@ package sha1 + + import ( + "bytes" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "encoding" ++ "errors" + "fmt" + "hash" + "io" +@@ -112,6 +113,9 @@ func testGoldenMarshal(t *testing.T) { + + state, err := h.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("could not marshal: %v", err) + continue + } +@@ -204,6 +208,9 @@ func testLargeHashes(t *testing.T) { + for i, test := range largeUnmarshalTests { + h := New() + if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("test %d could not unmarshal: %v", i, err) + continue + } +@@ -244,7 +251,7 @@ func TestSHA1Hash(t *testing.T) { + func TestExtraMethods(t *testing.T) { + h := maybeCloner(New()) + cryptotest.NoExtraMethods(t, &h, "ConstantTimeSum", +- "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + } + + func maybeCloner(h hash.Hash) any { +diff --git a/src/crypto/sha256/sha256.go b/src/crypto/sha256/sha256.go +index 069938a22dbc5a..8d0e06b86f4359 100644 +--- a/src/crypto/sha256/sha256.go ++++ b/src/crypto/sha256/sha256.go +@@ -8,7 +8,7 @@ package sha256 + + import ( + "crypto" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/sha256" + "hash" + ) +@@ -43,7 +43,7 @@ func New() hash.Hash { + // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal + // state of the hash. + func New224() hash.Hash { +- if boring.Enabled { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA224) { + return boring.NewSHA224() + } + return sha256.New224() +@@ -63,7 +63,7 @@ func Sum256(data []byte) [Size]byte { + + // Sum224 returns the SHA224 checksum of the data. + func Sum224(data []byte) [Size224]byte { +- if boring.Enabled { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA224) { + return boring.SHA224(data) + } + h := New224() +diff --git a/src/crypto/sha256/sha256_test.go b/src/crypto/sha256/sha256_test.go +index a18a536ba2896f..be543811ea94d1 100644 +--- a/src/crypto/sha256/sha256_test.go ++++ b/src/crypto/sha256/sha256_test.go +@@ -8,10 +8,13 @@ package sha256 + + import ( + "bytes" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "encoding" ++ "errors" + "fmt" + "hash" ++ "internal/systemcrypto" + "io" + "testing" + ) +@@ -163,6 +166,9 @@ func testGoldenMarshal(t *testing.T) { + + state, err := h.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("could not marshal: %v", err) + continue + } +@@ -256,6 +262,9 @@ func TestMarshalTypeMismatch(t *testing.T) { + + state1, err := h1.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("could not marshal: %v", err) + } + +@@ -325,10 +334,18 @@ func safeSum(h hash.Hash) (sum []byte, err error) { + return h.Sum(nil), nil + } + func TestLargeHashes(t *testing.T) { ++ if boring.Enabled { ++ if _, ok := New().(encoding.BinaryUnmarshaler); !ok { ++ t.Skip("BinaryUnmarshaler not implemented") ++ } ++ } + for i, test := range largeUnmarshalTests { + + h := New() + if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("test %d could not unmarshal: %v", i, err) + continue + } +@@ -347,6 +364,10 @@ func TestLargeHashes(t *testing.T) { + + func TestAllocations(t *testing.T) { + cryptotest.SkipTestAllocations(t) ++ expectedAllocations := 0.0 ++ if boring.Enabled { ++ expectedAllocations = 1 ++ } + if n := testing.AllocsPerRun(10, func() { + in := []byte("hello, world!") + out := make([]byte, 0, Size) +@@ -357,17 +378,10 @@ func TestAllocations(t *testing.T) { + h.Write(in) + out = h.Sum(out[:0]) + } +- { +- h := New224() +- h.Reset() +- h.Write(in) +- out = h.Sum(out[:0]) +- } + + Sum256(in) +- Sum224(in) +- }); n > 0 { +- t.Errorf("allocs = %v, want 0", n) ++ }); n > expectedAllocations { ++ t.Errorf("allocs = %v, want %v", n, expectedAllocations) + } + } + +@@ -404,13 +418,13 @@ func TestExtraMethods(t *testing.T) { + t.Run("SHA-224", func(t *testing.T) { + cryptotest.TestAllImplementations(t, "sha256", func(t *testing.T) { + h := maybeCloner(New224()) +- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + }) + }) + t.Run("SHA-256", func(t *testing.T) { + cryptotest.TestAllImplementations(t, "sha256", func(t *testing.T) { + h := maybeCloner(New()) +- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + }) + }) + } +@@ -481,7 +495,11 @@ func TestAllocatonsWithTypeAsserts(t *testing.T) { + marshaled, _ = h.(encoding.BinaryAppender).AppendBinary(marshaled[:0]) + h.(encoding.BinaryUnmarshaler).UnmarshalBinary(marshaled) + }) +- if allocs != 0 { +- t.Fatalf("allocs = %v; want = 0", allocs) ++ maxAllocs := 0 ++ if systemcrypto.Enabled() { ++ maxAllocs = 2 ++ } ++ if allocs > float64(maxAllocs) { ++ t.Fatalf("allocs = %v; want <= %v", allocs, maxAllocs) + } + } +diff --git a/src/crypto/sha3/sha3.go b/src/crypto/sha3/sha3.go +index f7c5549ddd113c..71f915d4c05045 100644 +--- a/src/crypto/sha3/sha3.go ++++ b/src/crypto/sha3/sha3.go +@@ -8,6 +8,7 @@ package sha3 + + import ( + "crypto" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/sha3" + "hash" + _ "unsafe" +@@ -22,6 +23,9 @@ func init() { + + // Sum224 returns the SHA3-224 hash of data. + func Sum224(data []byte) [28]byte { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_224) { ++ return boring.SumSHA3_224(data) ++ } + var out [28]byte + h := sha3.New224() + h.Write(data) +@@ -31,6 +35,9 @@ func Sum224(data []byte) [28]byte { + + // Sum256 returns the SHA3-256 hash of data. + func Sum256(data []byte) [32]byte { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_256) { ++ return boring.SumSHA3_256(data) ++ } + var out [32]byte + h := sha3.New256() + h.Write(data) +@@ -40,6 +47,9 @@ func Sum256(data []byte) [32]byte { + + // Sum384 returns the SHA3-384 hash of data. + func Sum384(data []byte) [48]byte { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_384) { ++ return boring.SumSHA3_384(data) ++ } + var out [48]byte + h := sha3.New384() + h.Write(data) +@@ -49,6 +59,9 @@ func Sum384(data []byte) [48]byte { + + // Sum512 returns the SHA3-512 hash of data. + func Sum512(data []byte) [64]byte { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_512) { ++ return boring.SumSHA3_512(data) ++ } + var out [64]byte + h := sha3.New512() + h.Write(data) +@@ -59,6 +72,9 @@ func Sum512(data []byte) [64]byte { + // SumSHAKE128 applies the SHAKE128 extendable output function to data and + // returns an output of the given length in bytes. + func SumSHAKE128(data []byte, length int) []byte { ++ if boring.Enabled && boring.SupportsSHAKE(128) { ++ return boring.SumSHAKE128(data, length) ++ } + // Outline the allocation for up to 256 bits of output to the caller's stack. + out := make([]byte, 32) + return sumSHAKE128(out, data, length) +@@ -79,6 +95,9 @@ func sumSHAKE128(out, data []byte, length int) []byte { + // SumSHAKE256 applies the SHAKE256 extendable output function to data and + // returns an output of the given length in bytes. + func SumSHAKE256(data []byte, length int) []byte { ++ if boring.Enabled && boring.SupportsSHAKE(256) { ++ return boring.SumSHAKE256(data, length) ++ } + // Outline the allocation for up to 512 bits of output to the caller's stack. + out := make([]byte, 64) + return sumSHAKE256(out, data, length) +@@ -99,37 +118,54 @@ func sumSHAKE256(out, data []byte, length int) []byte { + // SHA3 is an instance of a SHA-3 hash. It implements [hash.Hash]. + // The zero value is a usable SHA3-256 hash. + type SHA3 struct { +- s sha3.Digest ++ s sha3.Digest ++ boringS *boring.Hash ++ supported bool + } + + //go:linkname fips140hash_sha3Unwrap crypto/internal/fips140hash.sha3Unwrap +-func fips140hash_sha3Unwrap(s *SHA3) *sha3.Digest { ++func fips140hash_sha3Unwrap(s *SHA3) hash.Hash { + s.init() ++ if s.boringS != nil { ++ return s.boringS ++ } + return &s.s + } + + // New224 creates a new SHA3-224 hash. + func New224() *SHA3 { +- return &SHA3{*sha3.New224()} ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_224) { ++ return &SHA3{boringS: boring.NewSHA3_224(), supported: true} ++ } ++ return &SHA3{s: *sha3.New224()} + } + + // New256 creates a new SHA3-256 hash. + func New256() *SHA3 { +- return &SHA3{*sha3.New256()} ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_256) { ++ return &SHA3{boringS: boring.NewSHA3_256(), supported: true} ++ } ++ return &SHA3{s: *sha3.New256()} + } + + // New384 creates a new SHA3-384 hash. + func New384() *SHA3 { +- return &SHA3{*sha3.New384()} ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_384) { ++ return &SHA3{boringS: boring.NewSHA3_384(), supported: true} ++ } ++ return &SHA3{s: *sha3.New384()} + } + + // New512 creates a new SHA3-512 hash. + func New512() *SHA3 { +- return &SHA3{*sha3.New512()} ++ if boring.Enabled && boring.SupportsHash(crypto.SHA3_512) { ++ return &SHA3{boringS: boring.NewSHA3_512(), supported: true} ++ } ++ return &SHA3{s: *sha3.New512()} + } + + func (s *SHA3) init() { +- if s.s.Size() == 0 { ++ if s.s.Size() == 0 && s.boringS == nil { + *s = *New256() + } + } +@@ -137,53 +173,81 @@ func (s *SHA3) init() { + // Write absorbs more data into the hash's state. + func (s *SHA3) Write(p []byte) (n int, err error) { + s.init() ++ if boring.Enabled && s.supported { ++ return s.boringS.Write(p) ++ } + return s.s.Write(p) + } + + // Sum appends the current hash to b and returns the resulting slice. + func (s *SHA3) Sum(b []byte) []byte { + s.init() ++ if boring.Enabled && s.supported { ++ return s.boringS.Sum(b) ++ } + return s.s.Sum(b) + } + + // Reset resets the hash to its initial state. + func (s *SHA3) Reset() { + s.init() +- s.s.Reset() ++ if boring.Enabled && s.supported { ++ s.boringS.Reset() ++ } else { ++ s.s.Reset() ++ } + } + + // Size returns the number of bytes Sum will produce. + func (s *SHA3) Size() int { + s.init() ++ if boring.Enabled && s.supported { ++ return s.boringS.Size() ++ } + return s.s.Size() + } + + // BlockSize returns the hash's rate. + func (s *SHA3) BlockSize() int { + s.init() ++ if boring.Enabled && s.supported { ++ return s.boringS.BlockSize() ++ } + return s.s.BlockSize() + } + + // MarshalBinary implements [encoding.BinaryMarshaler]. + func (s *SHA3) MarshalBinary() ([]byte, error) { + s.init() ++ if boring.Enabled && s.supported { ++ return s.boringS.MarshalBinary() ++ } + return s.s.MarshalBinary() + } + + // AppendBinary implements [encoding.BinaryAppender]. + func (s *SHA3) AppendBinary(p []byte) ([]byte, error) { + s.init() ++ if boring.Enabled && s.supported { ++ return s.boringS.AppendBinary(p) ++ } + return s.s.AppendBinary(p) + } + + // UnmarshalBinary implements [encoding.BinaryUnmarshaler]. + func (s *SHA3) UnmarshalBinary(data []byte) error { + s.init() ++ if boring.Enabled && s.supported { ++ return s.boringS.UnmarshalBinary(data) ++ } + return s.s.UnmarshalBinary(data) + } + + // Clone implements [hash.Cloner]. + func (d *SHA3) Clone() (hash.Cloner, error) { ++ if boring.Enabled && d.supported { ++ return d.boringS.Clone() ++ } + r := *d + return &r, nil + } +@@ -191,23 +255,30 @@ func (d *SHA3) Clone() (hash.Cloner, error) { + // SHAKE is an instance of a SHAKE extendable output function. + // The zero value is a usable SHAKE256 hash. + type SHAKE struct { +- s sha3.SHAKE ++ s sha3.SHAKE ++ boringS *boring.SHAKE + } + + func (s *SHAKE) init() { +- if s.s.Size() == 0 { ++ if s.s.Size() == 0 && s.boringS == nil { + *s = *NewSHAKE256() + } + } + + // NewSHAKE128 creates a new SHAKE128 XOF. + func NewSHAKE128() *SHAKE { +- return &SHAKE{*sha3.NewShake128()} ++ if boring.Enabled && boring.SupportsSHAKE(128) { ++ return &SHAKE{boringS: boring.NewSHAKE128()} ++ } ++ return &SHAKE{s: *sha3.NewShake128()} + } + + // NewSHAKE256 creates a new SHAKE256 XOF. + func NewSHAKE256() *SHAKE { +- return &SHAKE{*sha3.NewShake256()} ++ if boring.Enabled && boring.SupportsSHAKE(256) { ++ return &SHAKE{boringS: boring.NewSHAKE256()} ++ } ++ return &SHAKE{s: *sha3.NewShake256()} + } + + // NewCSHAKE128 creates a new cSHAKE128 XOF. +@@ -216,7 +287,10 @@ func NewSHAKE256() *SHAKE { + // cSHAKE is desired. S is a customization byte string used for domain + // separation. When N and S are both empty, this is equivalent to NewSHAKE128. + func NewCSHAKE128(N, S []byte) *SHAKE { +- return &SHAKE{*sha3.NewCShake128(N, S)} ++ if boring.Enabled && boring.SupportsCSHAKE(128) { ++ return &SHAKE{boringS: boring.NewCSHAKE128(N, S)} ++ } ++ return &SHAKE{s: *sha3.NewCShake128(N, S)} + } + + // NewCSHAKE256 creates a new cSHAKE256 XOF. +@@ -225,7 +299,10 @@ func NewCSHAKE128(N, S []byte) *SHAKE { + // cSHAKE is desired. S is a customization byte string used for domain + // separation. When N and S are both empty, this is equivalent to NewSHAKE256. + func NewCSHAKE256(N, S []byte) *SHAKE { +- return &SHAKE{*sha3.NewCShake256(N, S)} ++ if boring.Enabled && boring.SupportsCSHAKE(256) { ++ return &SHAKE{boringS: boring.NewCSHAKE256(N, S)} ++ } ++ return &SHAKE{s: *sha3.NewCShake256(N, S)} + } + + // Write absorbs more data into the XOF's state. +@@ -233,6 +310,9 @@ func NewCSHAKE256(N, S []byte) *SHAKE { + // It panics if any output has already been read. + func (s *SHAKE) Write(p []byte) (n int, err error) { + s.init() ++ if boring.Enabled && s.boringS != nil { ++ return s.boringS.Write(p) ++ } + return s.s.Write(p) + } + +@@ -241,35 +321,54 @@ func (s *SHAKE) Write(p []byte) (n int, err error) { + // Any call to Write after a call to Read will panic. + func (s *SHAKE) Read(p []byte) (n int, err error) { + s.init() ++ if boring.Enabled && s.boringS != nil { ++ return s.boringS.Read(p) ++ } + return s.s.Read(p) + } + + // Reset resets the XOF to its initial state. + func (s *SHAKE) Reset() { + s.init() +- s.s.Reset() ++ if boring.Enabled && s.boringS != nil { ++ s.boringS.Reset() ++ } else { ++ s.s.Reset() ++ } + } + + // BlockSize returns the rate of the XOF. + func (s *SHAKE) BlockSize() int { + s.init() ++ if boring.Enabled && s.boringS != nil { ++ return s.boringS.BlockSize() ++ } + return s.s.BlockSize() + } + + // MarshalBinary implements [encoding.BinaryMarshaler]. + func (s *SHAKE) MarshalBinary() ([]byte, error) { + s.init() ++ if boring.Enabled && s.boringS != nil { ++ return s.boringS.MarshalBinary() ++ } + return s.s.MarshalBinary() + } + + // AppendBinary implements [encoding.BinaryAppender]. + func (s *SHAKE) AppendBinary(p []byte) ([]byte, error) { + s.init() ++ if boring.Enabled && s.boringS != nil { ++ return s.boringS.AppendBinary(p) ++ } + return s.s.AppendBinary(p) + } + + // UnmarshalBinary implements [encoding.BinaryUnmarshaler]. + func (s *SHAKE) UnmarshalBinary(data []byte) error { + s.init() ++ if boring.Enabled && s.boringS != nil { ++ return s.boringS.UnmarshalBinary(data) ++ } + return s.s.UnmarshalBinary(data) + } +diff --git a/src/crypto/sha3/sha3_test.go b/src/crypto/sha3/sha3_test.go +index 3973e0afd1b935..4a92e2652a50c4 100644 +--- a/src/crypto/sha3/sha3_test.go ++++ b/src/crypto/sha3/sha3_test.go +@@ -6,9 +6,11 @@ package sha3_test + + import ( + "bytes" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + . "crypto/sha3" + "encoding/hex" ++ "errors" + "hash" + "io" + "math/rand" +@@ -225,6 +227,10 @@ var sinkSHA3 byte + + func TestAllocations(t *testing.T) { + cryptotest.SkipTestAllocations(t) ++ expectedAllocations := 0.0 ++ if boring.Enabled { ++ expectedAllocations = 2 ++ } + t.Run("New", func(t *testing.T) { + if allocs := testing.AllocsPerRun(10, func() { + h := New256() +@@ -233,7 +239,7 @@ func TestAllocations(t *testing.T) { + out := make([]byte, 0, 32) + out = h.Sum(out) + sinkSHA3 ^= out[0] +- }); allocs > 0 { ++ }); allocs > expectedAllocations { + t.Errorf("expected zero allocations, got %0.1f", allocs) + } + }) +@@ -245,7 +251,7 @@ func TestAllocations(t *testing.T) { + out := make([]byte, 32) + h.Read(out) + sinkSHA3 ^= out[0] +- }); allocs > 0 { ++ }); allocs > expectedAllocations { + t.Errorf("expected zero allocations, got %0.1f", allocs) + } + }) +@@ -263,7 +269,7 @@ func TestAllocations(t *testing.T) { + b := []byte("ABC") + out := SumSHAKE128(b, 10) + sinkSHA3 ^= out[0] +- }); allocs > 0 { ++ }); allocs > expectedAllocations { + t.Errorf("expected zero allocations, got %0.1f", allocs) + } + }) +@@ -413,6 +419,9 @@ func testMarshalUnmarshal(t *testing.T, h *SHA3) { + h.Write(buf[:n]) + b, err := h.MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("MarshalBinary not supported") ++ } + t.Errorf("MarshalBinary: %v", err) + } + h.Write(bytes.Repeat([]byte{0}, 200)) +@@ -438,6 +447,9 @@ func testMarshalUnmarshalSHAKE(t *testing.T, h *SHAKE) { + h.Write(buf[:n]) + b, err := h.MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("MarshalBinary not supported") ++ } + t.Errorf("MarshalBinary: %v", err) + } + h.Write(bytes.Repeat([]byte{0}, 200)) +diff --git a/src/crypto/sha512/sha512.go b/src/crypto/sha512/sha512.go +index 1435eac1f5b5dc..ca424cb6b904ba 100644 +--- a/src/crypto/sha512/sha512.go ++++ b/src/crypto/sha512/sha512.go +@@ -12,7 +12,7 @@ package sha512 + + import ( + "crypto" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/sha512" + "hash" + ) +@@ -58,6 +58,9 @@ func New() hash.Hash { + // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal + // state of the hash. + func New512_224() hash.Hash { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA512_224) { ++ return boring.NewSHA512_224() ++ } + return sha512.New512_224() + } + +@@ -66,6 +69,9 @@ func New512_224() hash.Hash { + // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal + // state of the hash. + func New512_256() hash.Hash { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA512_256) { ++ return boring.NewSHA512_256() ++ } + return sha512.New512_256() + } + +@@ -106,6 +112,9 @@ func Sum384(data []byte) [Size384]byte { + + // Sum512_224 returns the Sum512/224 checksum of the data. + func Sum512_224(data []byte) [Size224]byte { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA512_224) { ++ return boring.SHA512_224(data) ++ } + h := New512_224() + h.Write(data) + var sum [Size224]byte +@@ -115,6 +124,9 @@ func Sum512_224(data []byte) [Size224]byte { + + // Sum512_256 returns the Sum512/256 checksum of the data. + func Sum512_256(data []byte) [Size256]byte { ++ if boring.Enabled && boring.SupportsHash(crypto.SHA512_256) { ++ return boring.SHA512_256(data) ++ } + h := New512_256() + h.Write(data) + var sum [Size256]byte +diff --git a/src/crypto/sha512/sha512_test.go b/src/crypto/sha512/sha512_test.go +index 080bf694f03652..389e2738a4e885 100644 +--- a/src/crypto/sha512/sha512_test.go ++++ b/src/crypto/sha512/sha512_test.go +@@ -8,9 +8,11 @@ package sha512 + + import ( + "bytes" ++ boring "crypto/internal/backend" + "crypto/internal/cryptotest" + "encoding" + "encoding/hex" ++ "errors" + "fmt" + "hash" + "io" +@@ -751,6 +753,9 @@ func testGoldenMarshal(t *testing.T) { + + state, err := h.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("could not marshal: %v", err) + return + } +@@ -807,6 +812,9 @@ func TestMarshalMismatch(t *testing.T) { + + state, err := h1.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("i=%d: could not marshal: %v", i, err) + continue + } +@@ -880,10 +888,18 @@ func safeSum(h hash.Hash) (sum []byte, err error) { + } + + func TestLargeHashes(t *testing.T) { ++ if boring.Enabled { ++ if _, ok := New().(encoding.BinaryUnmarshaler); !ok { ++ t.Skip("BinaryUnmarshaler not implemented") ++ } ++ } + for i, test := range largeUnmarshalTests { + + h := New() + if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Errorf("test %d could not unmarshal: %v", i, err) + continue + } +@@ -902,6 +918,10 @@ func TestLargeHashes(t *testing.T) { + + func TestAllocations(t *testing.T) { + cryptotest.SkipTestAllocations(t) ++ expectedAllocations := 0.0 ++ if boring.Enabled { ++ expectedAllocations = 10 ++ } + if n := testing.AllocsPerRun(10, func() { + in := []byte("hello, world!") + out := make([]byte, 0, Size) +@@ -935,8 +955,8 @@ func TestAllocations(t *testing.T) { + Sum384(in) + Sum512_224(in) + Sum512_256(in) +- }); n > 0 { +- t.Errorf("allocs = %v, want 0", n) ++ }); n > expectedAllocations { ++ t.Errorf("allocs = %v, want %v", n, expectedAllocations) + } + } + +@@ -967,25 +987,25 @@ func TestExtraMethods(t *testing.T) { + t.Run("SHA-384", func(t *testing.T) { + cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { + h := maybeCloner(New384()) +- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + }) + }) + t.Run("SHA-512/224", func(t *testing.T) { + cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { + h := maybeCloner(New512_224()) +- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + }) + }) + t.Run("SHA-512/256", func(t *testing.T) { + cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { + h := maybeCloner(New512_256()) +- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + }) + }) + t.Run("SHA-512", func(t *testing.T) { + cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { + h := maybeCloner(New()) +- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") ++ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") + }) + }) + } +diff --git a/src/crypto/systemcrypto_nocgo_linux.go b/src/crypto/systemcrypto_nocgo_linux.go +new file mode 100644 +index 00000000000000..b3a50c0aae95af +--- /dev/null ++++ b/src/crypto/systemcrypto_nocgo_linux.go +@@ -0,0 +1,18 @@ ++// Copyright 2025 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto && !(cgo || 386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64 || s390x) ++ ++package crypto ++ ++func init() { ++ ` ++ Using system crypto on Linux requires CGO_ENABLED=1. ++ ++ Our cgo-less OpenSSL implementation is only supported on certain architectures. ++ These architectures are: 386, amd64, arm, arm64, loong64, ppc64le, riscv64 and s390x. ++ ++ For more information, including how to request support for additional architectures, visit https://github.com/microsoft/go/blob/microsoft/main/eng/doc/MigrationGuide.md#cgo-is-not-enabled ++ ` ++} +diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go +index 1952be64fa952c..bad4a5093e3bbb 100644 +--- a/src/crypto/tls/cipher_suites.go ++++ b/src/crypto/tls/cipher_suites.go +@@ -10,7 +10,7 @@ import ( + "crypto/cipher" + "crypto/des" + "crypto/hmac" +- "crypto/internal/boring" ++ boring "crypto/internal/backend" + fipsaes "crypto/internal/fips140/aes" + "crypto/internal/fips140/aes/gcm" + "crypto/rc4" +@@ -568,7 +568,13 @@ func aeadChaCha20Poly1305(key, nonceMask []byte) aead { + if len(nonceMask) != aeadNonceLength { + panic("tls: internal error: wrong nonce length") + } +- aead, err := chacha20poly1305.New(key) ++ var aead cipher.AEAD ++ var err error ++ if boring.Enabled && boring.SupportsChaCha20Poly1305() { ++ aead, err = boring.NewChaCha20Poly1305(key) ++ } else { ++ aead, err = chacha20poly1305.New(key) ++ } + if err != nil { + panic(err) + } +diff --git a/src/crypto/tls/fipsonly/fipsonly.go b/src/crypto/tls/fipsonly/fipsonly.go +index e702f44e986746..e506a0d8841237 100644 +--- a/src/crypto/tls/fipsonly/fipsonly.go ++++ b/src/crypto/tls/fipsonly/fipsonly.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + // Package fipsonly restricts all TLS configuration to FIPS-approved settings. + // +diff --git a/src/crypto/tls/fipsonly/fipsonly_test.go b/src/crypto/tls/fipsonly/fipsonly_test.go +index 027bc22c33c921..eba08da985f832 100644 +--- a/src/crypto/tls/fipsonly/fipsonly_test.go ++++ b/src/crypto/tls/fipsonly/fipsonly_test.go +@@ -2,7 +2,7 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + +-//go:build boringcrypto ++//go:build goexperiment.systemcrypto + + package fipsonly + +diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go +index 52bf6087b62955..6d97d1fba0dfa9 100644 +--- a/src/crypto/tls/handshake_client.go ++++ b/src/crypto/tls/handshake_client.go +@@ -11,11 +11,11 @@ import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/hpke" +- "crypto/internal/fips140/tls13" + "crypto/mldsa" + "crypto/rsa" + "crypto/subtle" + "crypto/tls/internal/fips140tls" ++ "crypto/tls/internal/tls13" + "crypto/x509" + "errors" + "fmt" +@@ -524,7 +524,20 @@ func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error { + + // Does the handshake, either a full one or resumes old session. Requires hs.c, + // hs.hello, hs.serverHello, and, optionally, hs.session to be set. +-func (hs *clientHandshakeState) handshake() error { ++func (hs *clientHandshakeState) handshake() (err error) { ++ defer func() { ++ if err != nil { ++ return ++ } ++ if p := recover(); p != nil { ++ if err1, ok := p.(boringPRFError); ok { ++ err = err1.err ++ hs.c.sendAlert(alertInternalError) ++ } else { ++ panic(p) ++ } ++ } ++ }() + c := hs.c + + // If we did not load a session (hs.session == nil), but we did set a +diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go +index d367020c2066bf..6a83eda3573245 100644 +--- a/src/crypto/tls/handshake_client_tls13.go ++++ b/src/crypto/tls/handshake_client_tls13.go +@@ -10,9 +10,9 @@ import ( + "crypto" + "crypto/hkdf" + "crypto/hmac" +- "crypto/internal/fips140/tls13" + "crypto/rsa" + "crypto/subtle" ++ "crypto/tls/internal/tls13" + "errors" + "hash" + "slices" +diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go +index cde70463ef0fa4..4df9e6f5aac92c 100644 +--- a/src/crypto/tls/handshake_server.go ++++ b/src/crypto/tls/handshake_server.go +@@ -64,7 +64,20 @@ func (c *Conn) serverHandshake(ctx context.Context) error { + return hs.handshake() + } + +-func (hs *serverHandshakeState) handshake() error { ++func (hs *serverHandshakeState) handshake() (err error) { ++ defer func() { ++ if err != nil { ++ return ++ } ++ if p := recover(); p != nil { ++ if err1, ok := p.(boringPRFError); ok { ++ err = err1.err ++ hs.c.sendAlert(alertInternalError) ++ } else { ++ panic(p) ++ } ++ } ++ }() + c := hs.c + + if err := hs.processClientHello(); err != nil { +diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go +index 3175d74587d83a..ed7c2de8db51f1 100644 +--- a/src/crypto/tls/handshake_server_tls13.go ++++ b/src/crypto/tls/handshake_server_tls13.go +@@ -11,9 +11,9 @@ import ( + "crypto/hkdf" + "crypto/hmac" + "crypto/hpke" +- "crypto/internal/fips140/tls13" + "crypto/rsa" + "crypto/tls/internal/fips140tls" ++ "crypto/tls/internal/tls13" + "crypto/x509" + "errors" + "fmt" +@@ -453,9 +453,6 @@ func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash { + UnmarshalBinary(data []byte) error + } + marshaler, ok := in.(binaryMarshaler) +- if !ok { +- return nil +- } + state, err := marshaler.MarshalBinary() + if err != nil { + return nil +diff --git a/src/crypto/tls/internal/tls13/doc.go b/src/crypto/tls/internal/tls13/doc.go +new file mode 100644 +index 00000000000000..acfa551001af9c +--- /dev/null ++++ b/src/crypto/tls/internal/tls13/doc.go +@@ -0,0 +1,18 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++/* ++Microsoft build of Go only package. ++ ++TLS 1.3 Key Schedule is normally not part of the FIPS boundary, but upstream Go ++decided to include it in the FIPS boundary to facilitate the FIPS 140-3 certification ++process. ++ ++The problem is that crypto/internal/fips140/tls13 uses crypto/internal/fips140/hkdf, ++which can't be patched to use other backends. ++ ++To solve this problem, we created this package, which is a direct copy of ++crypto/internal/fips140/tls13, but uses crypto/hkdf instead of crypto/internal/fips140/hkdf. ++*/ ++package tls13 +diff --git a/src/crypto/tls/internal/tls13/tls13.go b/src/crypto/tls/internal/tls13/tls13.go +new file mode 100644 +index 00000000000000..579aaedabb188a +--- /dev/null ++++ b/src/crypto/tls/internal/tls13/tls13.go +@@ -0,0 +1,195 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++// Package tls13 implements the TLS 1.3 Key Schedule as specified in RFC 8446, ++// Section 7.1 and allowed by FIPS 140-3 IG 2.4.B Resolution 7. ++package tls13 ++ ++import ( ++ boring "crypto/internal/backend" ++ "crypto/internal/fips140hash" ++ ++ "crypto/hkdf" ++ "hash" ++ "internal/byteorder" ++) ++ ++// We don't set the service indicator in this package but we delegate that to ++// the underlying functions because the TLS 1.3 KDF does not have a standard of ++// its own. ++ ++// ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1. ++func ExpandLabel[H hash.Hash](hash func() H, secret []byte, label string, context []byte, length int) []byte { ++ if len("tls13 ")+len(label) > 255 || len(context) > 255 { ++ // It should be impossible for this to panic: labels are fixed strings, ++ // and context is either a fixed-length computed hash, or parsed from a ++ // field which has the same length limitation. ++ // ++ // Another reasonable approach might be to return a randomized slice if ++ // we encounter an error, which would break the connection, but avoid ++ // panicking. This would perhaps be safer but significantly more ++ // confusing to users. ++ panic("tls13: label or context too long") ++ } ++ ++ if boring.Enabled && boring.SupportsTLS13KDF() { ++ fh := fips140hash.UnwrapNew(hash) ++ key, err := boring.ExpandTLS13KDF(fh, secret, []byte(label), context, length) ++ if err != nil { ++ panic(err) ++ } ++ return key ++ } ++ ++ hkdfLabel := make([]byte, 0, 2+1+len("tls13 ")+len(label)+1+len(context)) ++ hkdfLabel = byteorder.BEAppendUint16(hkdfLabel, uint16(length)) ++ hkdfLabel = append(hkdfLabel, byte(len("tls13 ")+len(label))) ++ hkdfLabel = append(hkdfLabel, "tls13 "...) ++ hkdfLabel = append(hkdfLabel, label...) ++ hkdfLabel = append(hkdfLabel, byte(len(context))) ++ hkdfLabel = append(hkdfLabel, context...) ++ key, err := hkdf.Expand(hash, secret, string(hkdfLabel), length) ++ if err != nil { ++ panic(err) ++ } ++ return key ++} ++ ++func extract[H hash.Hash](hash func() H, newSecret, currentSecret []byte) []byte { ++ if newSecret == nil { ++ newSecret = make([]byte, hash().Size()) ++ } ++ prk, err := hkdf.Extract(hash, newSecret, currentSecret) ++ if err != nil { ++ panic(err) ++ } ++ return prk ++} ++ ++func deriveSecret[H hash.Hash](hash func() H, secret []byte, label string, transcript hash.Hash) []byte { ++ if transcript == nil { ++ transcript = hash() ++ } ++ return ExpandLabel(hash, secret, label, transcript.Sum(nil), transcript.Size()) ++} ++ ++const ( ++ resumptionBinderLabel = "res binder" ++ clientEarlyTrafficLabel = "c e traffic" ++ clientHandshakeTrafficLabel = "c hs traffic" ++ serverHandshakeTrafficLabel = "s hs traffic" ++ clientApplicationTrafficLabel = "c ap traffic" ++ serverApplicationTrafficLabel = "s ap traffic" ++ earlyExporterLabel = "e exp master" ++ exporterLabel = "exp master" ++ resumptionLabel = "res master" ++) ++ ++type EarlySecret struct { ++ secret []byte ++ hash func() hash.Hash ++} ++ ++func NewEarlySecret[H hash.Hash](h func() H, psk []byte) *EarlySecret { ++ return &EarlySecret{ ++ secret: extract(h, psk, nil), ++ hash: func() hash.Hash { return h() }, ++ } ++} ++ ++func (s *EarlySecret) ResumptionBinderKey() []byte { ++ return deriveSecret(s.hash, s.secret, resumptionBinderLabel, nil) ++} ++ ++// ClientEarlyTrafficSecret derives the client_early_traffic_secret from the ++// early secret and the transcript up to the ClientHello. ++func (s *EarlySecret) ClientEarlyTrafficSecret(transcript hash.Hash) []byte { ++ return deriveSecret(s.hash, s.secret, clientEarlyTrafficLabel, transcript) ++} ++ ++type HandshakeSecret struct { ++ secret []byte ++ hash func() hash.Hash ++} ++ ++func (s *EarlySecret) HandshakeSecret(sharedSecret []byte) *HandshakeSecret { ++ derived := deriveSecret(s.hash, s.secret, "derived", nil) ++ return &HandshakeSecret{ ++ secret: extract(s.hash, sharedSecret, derived), ++ hash: s.hash, ++ } ++} ++ ++// ClientHandshakeTrafficSecret derives the client_handshake_traffic_secret from ++// the handshake secret and the transcript up to the ServerHello. ++func (s *HandshakeSecret) ClientHandshakeTrafficSecret(transcript hash.Hash) []byte { ++ return deriveSecret(s.hash, s.secret, clientHandshakeTrafficLabel, transcript) ++} ++ ++// ServerHandshakeTrafficSecret derives the server_handshake_traffic_secret from ++// the handshake secret and the transcript up to the ServerHello. ++func (s *HandshakeSecret) ServerHandshakeTrafficSecret(transcript hash.Hash) []byte { ++ return deriveSecret(s.hash, s.secret, serverHandshakeTrafficLabel, transcript) ++} ++ ++type MasterSecret struct { ++ secret []byte ++ hash func() hash.Hash ++} ++ ++func (s *HandshakeSecret) MasterSecret() *MasterSecret { ++ derived := deriveSecret(s.hash, s.secret, "derived", nil) ++ return &MasterSecret{ ++ secret: extract(s.hash, nil, derived), ++ hash: s.hash, ++ } ++} ++ ++// ClientApplicationTrafficSecret derives the client_application_traffic_secret_0 ++// from the master secret and the transcript up to the server Finished. ++func (s *MasterSecret) ClientApplicationTrafficSecret(transcript hash.Hash) []byte { ++ return deriveSecret(s.hash, s.secret, clientApplicationTrafficLabel, transcript) ++} ++ ++// ServerApplicationTrafficSecret derives the server_application_traffic_secret_0 ++// from the master secret and the transcript up to the server Finished. ++func (s *MasterSecret) ServerApplicationTrafficSecret(transcript hash.Hash) []byte { ++ return deriveSecret(s.hash, s.secret, serverApplicationTrafficLabel, transcript) ++} ++ ++// ResumptionMasterSecret derives the resumption_master_secret from the master secret ++// and the transcript up to the client Finished. ++func (s *MasterSecret) ResumptionMasterSecret(transcript hash.Hash) []byte { ++ return deriveSecret(s.hash, s.secret, resumptionLabel, transcript) ++} ++ ++type ExporterMasterSecret struct { ++ secret []byte ++ hash func() hash.Hash ++} ++ ++// ExporterMasterSecret derives the exporter_master_secret from the master secret ++// and the transcript up to the server Finished. ++func (s *MasterSecret) ExporterMasterSecret(transcript hash.Hash) *ExporterMasterSecret { ++ return &ExporterMasterSecret{ ++ secret: deriveSecret(s.hash, s.secret, exporterLabel, transcript), ++ hash: s.hash, ++ } ++} ++ ++// EarlyExporterMasterSecret derives the exporter_master_secret from the early secret ++// and the transcript up to the ClientHello. ++func (s *EarlySecret) EarlyExporterMasterSecret(transcript hash.Hash) *ExporterMasterSecret { ++ return &ExporterMasterSecret{ ++ secret: deriveSecret(s.hash, s.secret, earlyExporterLabel, transcript), ++ hash: s.hash, ++ } ++} ++ ++func (s *ExporterMasterSecret) Exporter(label string, context []byte, length int) []byte { ++ secret := deriveSecret(s.hash, s.secret, label, nil) ++ h := s.hash() ++ h.Write(context) ++ return ExpandLabel(s.hash, secret, "exporter", h.Sum(nil), length) ++} +diff --git a/src/crypto/tls/key_schedule.go b/src/crypto/tls/key_schedule.go +index 652a6489b899d6..36528e93156bef 100644 +--- a/src/crypto/tls/key_schedule.go ++++ b/src/crypto/tls/key_schedule.go +@@ -9,8 +9,8 @@ import ( + "crypto/ecdh" + "crypto/fips140" + "crypto/hmac" +- "crypto/internal/fips140/tls13" + "crypto/mlkem" ++ "crypto/tls/internal/tls13" + "errors" + "hash" + "io" +diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go +index bc59300f41c762..463ae16480f2be 100644 +--- a/src/crypto/tls/prf.go ++++ b/src/crypto/tls/prf.go +@@ -7,6 +7,7 @@ package tls + import ( + "crypto" + "crypto/hmac" ++ boring "crypto/internal/backend" + "crypto/internal/fips140/tls12" + "crypto/md5" + "crypto/sha1" +@@ -47,9 +48,25 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { + } + } + ++// boringPRFError can happen, for example, if the seed is too large. The Go implementation doesn't limit the seed size, ++// as RFC 5705 doesn't specify a limit, but stock OpenSSL restrict it to 1024 and CNG to 256. ++type boringPRFError struct { ++ err error ++} ++ ++func (e *boringPRFError) Error() string { ++ return e.err.Error() ++} ++ + // prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5. + func prf10(secret []byte, label string, seed []byte, keyLen int) []byte { + result := make([]byte, keyLen) ++ if boring.Enabled && boring.SupportsTLS1PRF() { ++ if err := boring.TLS1PRF(result, secret, []byte(label), seed, nil); err != nil { ++ panic(boringPRFError{fmt.Errorf("crypto/tls: prf10: %v", err)}) ++ } ++ return result ++ } + hashSHA1 := sha1.New + hashMD5 := md5.New + +@@ -72,6 +89,13 @@ func prf10(secret []byte, label string, seed []byte, keyLen int) []byte { + // prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. + func prf12(hashFunc func() hash.Hash) prfFunc { + return func(secret []byte, label string, seed []byte, keyLen int) []byte { ++ if boring.Enabled && boring.SupportsTLS1PRF() { ++ result := make([]byte, keyLen) ++ if err := boring.TLS1PRF(result, secret, []byte(label), seed, hashFunc); err != nil { ++ panic(boringPRFError{fmt.Errorf("crypto/tls: prf12: %v", err)}) ++ } ++ return result ++ } + return tls12.PRF(hashFunc, secret, label, seed, keyLen) + } + } +diff --git a/src/crypto/tls/prf_test.go b/src/crypto/tls/prf_test.go +index 8233985a62bd22..ef1b2bfe02c3b7 100644 +--- a/src/crypto/tls/prf_test.go ++++ b/src/crypto/tls/prf_test.go +@@ -5,7 +5,10 @@ + package tls + + import ( ++ "crypto/fips140" ++ boring "crypto/internal/backend" + "encoding/hex" ++ "runtime" + "testing" + ) + +@@ -46,6 +49,12 @@ type testKeysFromTest struct { + } + + func TestKeysFromPreMasterSecret(t *testing.T) { ++ if boring.Enabled && runtime.GOOS == "linux" && fips140.Enabled() { ++ // Some OpenSSL providers don't support TLS 1.0 PRF in FIPS mode. ++ // The public crypto/tls API doesn't support it either, but these tests ++ // use internal functions. ++ t.Skip("Skipping test on systemcrypto FIPS builds") ++ } + for i, test := range testKeysFromTests { + in, _ := hex.DecodeString(test.preMasterSecret) + clientRandom, _ := hex.DecodeString(test.clientRandom) +diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go +index 1d3e845d0f0a9c..eb4318faf13f7b 100644 +--- a/src/crypto/x509/verify_test.go ++++ b/src/crypto/x509/verify_test.go +@@ -3127,7 +3127,7 @@ func dsaSelfSignedCNX(t *testing.T) []byte { + t.Helper() + var params dsa.Parameters + if err := dsa.GenerateParameters(¶ms, rand.Reader, dsa.L1024N160); err != nil { +- t.Fatal(err) ++ t.Skip(err) // CBL-Mariner doesn't support dsa.L1024N160, skip test + } + + var dsaPriv dsa.PrivateKey +diff --git a/src/go/build/buildbackend_test.go b/src/go/build/buildbackend_test.go +new file mode 100644 +index 00000000000000..ffb835ce34a2f7 +--- /dev/null ++++ b/src/go/build/buildbackend_test.go +@@ -0,0 +1,50 @@ ++// Copyright 2023 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package build ++ ++import ( ++ "reflect" ++ "testing" ++) ++ ++// Check that the systemcrypto tag works and collects AllTags correctly. ++// This is based on the TestAllTags test. ++func TestCryptoBackendAllTags(t *testing.T) { ++ ctxt := Default ++ // Remove tool tags so these tests behave the same regardless of the ++ // goexperiments that happen to be set during the run. ++ ctxt.ToolTags = []string{} ++ ctxt.GOARCH = "amd64" ++ ctxt.GOOS = "linux" ++ ctxt.BuildTags = []string{"goexperiment.systemcrypto"} ++ ++ p, err := ctxt.ImportDir("testdata/backendtags_system", 0) ++ if err != nil { ++ t.Fatal(err) ++ } ++ want := []string{"goexperiment.systemcrypto"} ++ if !reflect.DeepEqual(p.AllTags, want) { ++ t.Errorf("AllTags = %v, want %v", p.AllTags, want) ++ } ++ wantFiles := []string{"main.go", "systemcrypto.go"} ++ if !reflect.DeepEqual(p.GoFiles, wantFiles) { ++ t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) ++ } ++ ++ // Test without systemcrypto - only main.go should be included ++ ctxt.BuildTags = []string{} ++ p, err = ctxt.ImportDir("testdata/backendtags_system", 0) ++ if err != nil { ++ t.Fatal(err) ++ } ++ want = []string{"goexperiment.systemcrypto"} ++ if !reflect.DeepEqual(p.AllTags, want) { ++ t.Errorf("AllTags = %v, want %v", p.AllTags, want) ++ } ++ wantFiles = []string{"main.go"} ++ if !reflect.DeepEqual(p.GoFiles, wantFiles) { ++ t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) ++ } ++} +diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go +index a71759adcb7363..ffe6279a6942d7 100644 +--- a/src/go/build/deps_test.go ++++ b/src/go/build/deps_test.go +@@ -372,8 +372,10 @@ var depsRules = ` + math/big, go/token + < go/constant; + +- FMT, internal/goexperiment ++ FMT, internal/goexperiment, internal/platform + < internal/buildcfg; ++ internal/buildcfg ++ < internal/systemcrypto; + + # The vast majority of standard library packages should not be resorting to regexp. + # go/types is a good chokepoint. It shouldn't use regexp, nor should anything +@@ -537,7 +539,7 @@ var depsRules = ` + < crypto/internal/fips140/edwards25519 + < crypto/internal/fips140/ed25519 + < crypto/internal/fips140/rsa +- < crypto/fips140 < FIPS; ++ < FIPS; + + crypto !< FIPS; + +@@ -557,7 +559,20 @@ var depsRules = ` + < github.com/microsoft/go-crypto-winnative/internal/sysdll + < github.com/microsoft/go-crypto-winnative/internal/bcrypt; + +- FIPS, internal/godebug, embed ++ github.com/microsoft/go-crypto-openssl/osslsetup ++ < crypto/internal/backend/internal/opensslsetup; ++ ++ internal/godebug, syscall, internal/syscall/windows/sysdll, ++ crypto/internal/backend/internal/opensslsetup ++ < crypto/internal/backend/internal/fips140state ++ < crypto/internal/backend/fips140; ++ ++ internal/godebug, crypto/internal/fips140, crypto/internal/fips140/check, ++ crypto/internal/backend/fips140 ++ < crypto/fips140; ++ ++ FIPS, internal/godebug, embed, ++ crypto/fips140 + < crypto/internal/fips140only + < crypto + < crypto/subtle +@@ -577,6 +592,14 @@ var depsRules = ` + github.com/microsoft/go-crypto-winnative/internal/bcrypt + < github.com/microsoft/go-crypto-winnative/cng; + ++ github.com/microsoft/go-crypto-openssl/openssl, ++ github.com/microsoft/go-crypto-winnative/cng, ++ github.com/microsoft/go-crypto-darwin/xcrypto, ++ crypto/internal/backend/internal/fips140state, ++ crypto/internal/backend/fips140, ++ crypto/internal/boring/sig ++ < crypto/internal/backend; ++ + FIPS, internal/godebug, embed, + crypto/internal/boring/sig, + crypto/internal/boring/syso, +@@ -584,7 +601,8 @@ var depsRules = ` + crypto/internal/fips140only, + crypto, + crypto/subtle, +- crypto/cipher ++ crypto/cipher, ++ crypto/internal/backend + < crypto/sha3 + < crypto/internal/fips140hash + < crypto/internal/boring +@@ -603,6 +621,7 @@ var depsRules = ` + crypto/ecdh, + crypto/mlkem, + crypto/mldsa ++ < crypto/tls/internal/tls13 + < CRYPTO; + + CRYPTO +@@ -616,8 +636,15 @@ var depsRules = ` + math/big, github.com/microsoft/go-crypto-darwin/xcrypto < github.com/microsoft/go-crypto-darwin/bbig; + math/big, github.com/microsoft/go-crypto-winnative/cng < github.com/microsoft/go-crypto-winnative/cng/bbig; + +- CRYPTO, FMT, math/big, internal/saferio +- < crypto/internal/boring/bbig ++ FMT, math/big, crypto/internal/boring ++ < crypto/internal/boring/bbig; ++ ++ CRYPTO, FMT, math/big, internal/saferio, ++ crypto/internal/boring/bbig, ++ github.com/microsoft/go-crypto-openssl/bbig, ++ github.com/microsoft/go-crypto-darwin/bbig, ++ github.com/microsoft/go-crypto-winnative/cng/bbig ++ < crypto/internal/backend/bbig + < crypto/internal/fips140cache + < crypto/rand + < crypto/ed25519 # depends on crypto/rand.Reader +diff --git a/src/go/build/testdata/backendtags_system/main.go b/src/go/build/testdata/backendtags_system/main.go +new file mode 100644 +index 00000000000000..38dd16da61accb +--- /dev/null ++++ b/src/go/build/testdata/backendtags_system/main.go +@@ -0,0 +1,3 @@ ++package main ++ ++func main() {} +diff --git a/src/go/build/testdata/backendtags_system/systemcrypto.go b/src/go/build/testdata/backendtags_system/systemcrypto.go +new file mode 100644 +index 00000000000000..eb8a026982259c +--- /dev/null ++++ b/src/go/build/testdata/backendtags_system/systemcrypto.go +@@ -0,0 +1,3 @@ ++//go:build goexperiment.systemcrypto ++ ++package main +diff --git a/src/hash/boring_test.go b/src/hash/boring_test.go +new file mode 100644 +index 00000000000000..52748c44698076 +--- /dev/null ++++ b/src/hash/boring_test.go +@@ -0,0 +1,9 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build goexperiment.systemcrypto ++ ++package hash_test ++ ++const boringEnabled = true +diff --git a/src/hash/example_test.go b/src/hash/example_test.go +index f07b9aaa2c4898..b380537215634d 100644 +--- a/src/hash/example_test.go ++++ b/src/hash/example_test.go +@@ -2,6 +2,8 @@ + // Use of this source code is governed by a BSD-style + // license that can be found in the LICENSE file. + ++//go:build !goexperiment.systemcrypto ++ + package hash_test + + import ( +diff --git a/src/hash/marshal_test.go b/src/hash/marshal_test.go +index 3091f7a67acede..fa3f0fd82cfd30 100644 +--- a/src/hash/marshal_test.go ++++ b/src/hash/marshal_test.go +@@ -16,6 +16,7 @@ import ( + "crypto/sha512" + "encoding" + "encoding/hex" ++ "errors" + "hash" + "hash/adler32" + "hash/crc32" +@@ -80,6 +81,9 @@ func TestMarshalHash(t *testing.T) { + } + enc, err := h2m.MarshalBinary() + if err != nil { ++ if errors.Is(err, errors.ErrUnsupported) { ++ t.Skip("BinaryMarshaler not supported") ++ } + t.Fatalf("MarshalBinary: %v", err) + } + if !bytes.Equal(enc, tt.golden) { +diff --git a/src/hash/notboring_test.go b/src/hash/notboring_test.go +new file mode 100644 +index 00000000000000..11dc691600b110 +--- /dev/null ++++ b/src/hash/notboring_test.go +@@ -0,0 +1,9 @@ ++// Copyright 2024 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build !goexperiment.systemcrypto ++ ++package hash_test ++ ++const boringEnabled = false +diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go +index 89fd74eb823162..6d266ff641d721 100644 +--- a/src/internal/buildcfg/cfg.go ++++ b/src/internal/buildcfg/cfg.go +@@ -38,6 +38,8 @@ var ( + Version = version + ) + ++var SystemCryptoDisabled = os.Getenv("MS_GO_NOSYSTEMCRYPTO") == "1" ++ + // Error is one of the errors found (if any) in the build configuration. + var Error error + +diff --git a/src/internal/buildcfg/cfg_test.go b/src/internal/buildcfg/cfg_test.go +index 2bbd478280241e..77eaacdc38756d 100644 +--- a/src/internal/buildcfg/cfg_test.go ++++ b/src/internal/buildcfg/cfg_test.go +@@ -6,6 +6,8 @@ package buildcfg + + import ( + "os" ++ "slices" ++ "strings" + "testing" + ) + +@@ -128,6 +130,48 @@ func TestGogoarchTags(t *testing.T) { + GOARM64 = old_goarm64 + } + ++func TestSystemCryptoNotGoExperiment(t *testing.T) { ++ flags, err := ParseGOEXPERIMENT("windows", "amd64", "") ++ if err != nil { ++ t.Fatal(err) ++ } ++ if slices.Contains(flags.Enabled(), "systemcrypto") { ++ t.Fatalf("Enabled() = %v, want no systemcrypto", flags.Enabled()) ++ } ++ if got := flags.String(); got != "" { ++ t.Fatalf("String() = %q, want empty string", got) ++ } ++ ++ if slices.Contains(flags.All(), "systemcrypto") || slices.Contains(flags.All(), "nosystemcrypto") { ++ t.Fatalf("All() = %v, want no systemcrypto setting", flags.All()) ++ } ++ for _, exp := range []string{"opensslcrypto", "cngcrypto", "darwincrypto"} { ++ if slices.Contains(flags.All(), exp) || slices.Contains(flags.All(), "no"+exp) { ++ t.Fatalf("All() = %v, want no %s setting", flags.All(), exp) ++ } ++ if _, err := ParseGOEXPERIMENT("windows", "386", exp); err == nil { ++ t.Fatalf("ParseGOEXPERIMENT accepted %s", exp) ++ } ++ } ++ if _, err := ParseGOEXPERIMENT("windows", "386", "systemcrypto"); err == nil { ++ t.Fatal("ParseGOEXPERIMENT accepted systemcrypto") ++ } else if !strings.Contains(err.Error(), "enabled automatically") || !strings.Contains(err.Error(), "MS_GO_NOSYSTEMCRYPTO=1") { ++ t.Fatalf("ParseGOEXPERIMENT systemcrypto error = %q", err) ++ } ++ if _, err := ParseGOEXPERIMENT("windows", "amd64", "nosystemcrypto"); err == nil { ++ t.Fatal("ParseGOEXPERIMENT accepted nosystemcrypto") ++ } else if !strings.Contains(err.Error(), "MS_GO_NOSYSTEMCRYPTO=1") || !strings.Contains(err.Error(), "systemcrypto supports CGO_ENABLED=0 since Go 1.27") { ++ t.Fatalf("ParseGOEXPERIMENT nosystemcrypto error = %q", err) ++ } ++ ++ t.Setenv("CGO_ENABLED", "0") ++ if _, err := ParseGOEXPERIMENT("windows", "amd64", "systemcrypto"); err == nil { ++ t.Fatal("ParseGOEXPERIMENT accepted systemcrypto with CGO_ENABLED=0") ++ } else if strings.Contains(err.Error(), "systemcrypto supports CGO_ENABLED=0 since Go 1.27") { ++ t.Fatalf("ParseGOEXPERIMENT systemcrypto CGO_ENABLED=0 error = %q", err) ++ } ++} ++ + var goodFIPS = []string{ + "v1.0.0", + "v1.0.1", +diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go +index 8eeef17c1f470e..dda96ef5b15616 100644 +--- a/src/internal/buildcfg/exp.go ++++ b/src/internal/buildcfg/exp.go +@@ -10,12 +10,14 @@ import ( + "strings" + + "internal/goexperiment" ++ goplatform "internal/platform" + ) + + // ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline + // (platform-default) experiment configuration. + type ExperimentFlags struct { + goexperiment.Flags ++ + baseline goexperiment.Flags + } + +@@ -131,6 +133,21 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { + if strings.HasPrefix(f, "no") { + f, val = f[2:], false + } ++ // Check for removed crypto experiments. ++ switch f { ++ case "systemcrypto": ++ if val { ++ return nil, fmt.Errorf("GOEXPERIMENT=systemcrypto has been removed; system crypto is enabled automatically on supported platforms and can be disabled with MS_GO_NOSYSTEMCRYPTO=1") ++ } ++ return nil, fmt.Errorf("GOEXPERIMENT=nosystemcrypto has been removed; use MS_GO_NOSYSTEMCRYPTO=1 to disable system crypto; note that systemcrypto supports CGO_ENABLED=0 since Go 1.27") ++ case "opensslcrypto", "cngcrypto", "darwincrypto": ++ switch { ++ case goplatform.SystemCryptoSupported(goos, goarch): ++ return nil, fmt.Errorf("systemcrypto is enabled by default, GOEXPERIMENT %s is not necessary", f) ++ default: ++ return nil, fmt.Errorf("GOEXPERIMENT %s has been removed; system crypto is enabled automatically on supported platforms", f) ++ } ++ } + set, ok := names[f] + if !ok { + return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f) +@@ -152,6 +169,9 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { + if flags.RegabiArgs && !flags.RegabiWrappers { + return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers") + } ++ if flags.BoringCrypto { ++ return nil, fmt.Errorf("GOEXPERIMENT boringcrypto is not supported in the Microsoft build of Go") ++ } + return flags, nil + } + +diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go +index 9329769721b7de..1724afb71b851e 100644 +--- a/src/internal/cfg/cfg.go ++++ b/src/internal/cfg/cfg.go +@@ -70,5 +70,6 @@ const KnownEnv = ` + GOWASM + GOWORK + GO_EXTLINK_ENABLED ++ MS_GO_NOSYSTEMCRYPTO + PKG_CONFIG + ` +diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go +index 6f37e368596498..471c5cc430bb29 100644 +--- a/src/internal/platform/supported.go ++++ b/src/internal/platform/supported.go +@@ -279,3 +279,15 @@ func FirstClass(goos, goarch string) bool { + func Broken(goos, goarch string) bool { + return distInfo[OSArch{goos, goarch}].Broken + } ++ ++// SystemCryptoSupported reports whether goos/goarch supports system crypto. ++func SystemCryptoSupported(goos, goarch string) bool { ++ switch goos { ++ case "linux", "darwin": ++ return true ++ case "windows": ++ return goarch == "amd64" || goarch == "arm64" ++ default: ++ return false ++ } ++} +diff --git a/src/internal/systemcrypto/systemcrypto.go b/src/internal/systemcrypto/systemcrypto.go +new file mode 100644 +index 00000000000000..71c1923a270646 +--- /dev/null ++++ b/src/internal/systemcrypto/systemcrypto.go +@@ -0,0 +1,20 @@ ++// Copyright 2026 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package systemcrypto ++ ++import ( ++ "internal/buildcfg" ++ "internal/platform" ++) ++ ++// Enabled reports whether system crypto is enabled for the current build target. ++func Enabled() bool { ++ return EnabledFor(buildcfg.GOOS, buildcfg.GOARCH) ++} ++ ++// EnabledFor reports whether system crypto is enabled for goos/goarch. ++func EnabledFor(goos, goarch string) bool { ++ return platform.SystemCryptoSupported(goos, goarch) && !buildcfg.SystemCryptoDisabled ++} +diff --git a/src/internal/systemcrypto/systemcrypto_test.go b/src/internal/systemcrypto/systemcrypto_test.go +new file mode 100644 +index 00000000000000..c37e436bf274e4 +--- /dev/null ++++ b/src/internal/systemcrypto/systemcrypto_test.go +@@ -0,0 +1,58 @@ ++// Copyright 2026 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package systemcrypto ++ ++import ( ++ "internal/buildcfg" ++ "internal/platform" ++ "testing" ++) ++ ++func TestEnabledFor(t *testing.T) { ++ old := buildcfg.SystemCryptoDisabled ++ t.Cleanup(func() { buildcfg.SystemCryptoDisabled = old }) ++ ++ buildcfg.SystemCryptoDisabled = false ++ tests := []struct { ++ goos string ++ goarch string ++ want bool ++ }{ ++ {"linux", "amd64", true}, ++ {"linux", "386", true}, ++ {"darwin", "arm64", true}, ++ {"windows", "amd64", true}, ++ {"windows", "386", false}, ++ {"freebsd", "amd64", false}, ++ } ++ for _, tt := range tests { ++ if got := EnabledFor(tt.goos, tt.goarch); got != tt.want { ++ t.Errorf("EnabledFor(%q, %q) = %v, want %v", tt.goos, tt.goarch, got, tt.want) ++ } ++ } ++ ++ buildcfg.SystemCryptoDisabled = true ++ for _, tt := range tests { ++ if got := EnabledFor(tt.goos, tt.goarch); got { ++ t.Errorf("EnabledFor(%q, %q) with SystemCryptoDisabled = true = %v, want false", tt.goos, tt.goarch, got) ++ } ++ } ++} ++ ++func TestEnabledUsesBuildConfig(t *testing.T) { ++ old := buildcfg.SystemCryptoDisabled ++ t.Cleanup(func() { buildcfg.SystemCryptoDisabled = old }) ++ ++ buildcfg.SystemCryptoDisabled = false ++ want := platform.SystemCryptoSupported(buildcfg.GOOS, buildcfg.GOARCH) ++ if got := Enabled(); got != want { ++ t.Fatalf("Enabled() = %v, want %v", got, want) ++ } ++ ++ buildcfg.SystemCryptoDisabled = true ++ if got := Enabled(); got { ++ t.Fatalf("Enabled() with SystemCryptoDisabled = true = %v, want false", got) ++ } ++} +diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go +index 42211ed099ed1e..e6d185374ffe6d 100644 +--- a/src/net/lookup_test.go ++++ b/src/net/lookup_test.go +@@ -1500,6 +1500,9 @@ func TestLookupPortIPNetworkString(t *testing.T) { + } + + func TestLookupNoSuchHost(t *testing.T) { ++ if runtime.GOOS == "darwin" { ++ t.Skip("skipping on darwin; see https://github.com/microsoft/go/issues/1394") ++ } + mustHaveExternalNetwork(t) + + const testNXDOMAIN = "invalid.invalid." +diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go +index 4f39d9bbe345a8..b372f4077a3fe3 100644 +--- a/src/os/exec/exec_test.go ++++ b/src/os/exec/exec_test.go +@@ -15,6 +15,7 @@ import ( + "flag" + "fmt" + "internal/poll" ++ "internal/systemcrypto" + "internal/testenv" + "io" + "log" +@@ -692,6 +693,14 @@ func TestExtraFiles(t *testing.T) { + t.Skipf("skipping test on %q", runtime.GOOS) + } + ++ if systemcrypto.Enabled() && runtime.GOOS == "linux" { ++ // OpenSSL default behavior is to maintain open FDs to any ++ // random devices that get used by the random number library. ++ // Since those FDs are not marked FD_CLOEXEC or O_CLOEXEC, ++ // they also get inherited by children. ++ t.Skip("skipping test because test was run with systemcrypto on linux (OpenSSL)") ++ } ++ + // Force network usage, to verify the epoll (or whatever) fd + // doesn't leak to the child, + ln, err := net.Listen("tcp", "127.0.0.1:0") +diff --git a/src/runtime/runtime_boring.go b/src/runtime/runtime_boring.go +index 831ee67afc952d..d2f00d57c10286 100644 +--- a/src/runtime/runtime_boring.go ++++ b/src/runtime/runtime_boring.go +@@ -14,3 +14,8 @@ func boring_runtime_arg0() string { + } + return argslice[0] + } ++ ++//go:linkname crypto_backend_runtime_arg0 crypto/internal/backend.runtime_arg0 ++func crypto_backend_runtime_arg0() string { ++ return boring_runtime_arg0() ++} diff --git a/patches/0005-Disable-GOTOOLCHAIN-support.patch b/patches/0003-Disable-GOTOOLCHAIN-support.patch similarity index 100% rename from patches/0005-Disable-GOTOOLCHAIN-support.patch rename to patches/0003-Disable-GOTOOLCHAIN-support.patch diff --git a/patches/0003-Implement-crypto-internal-backend.patch b/patches/0003-Implement-crypto-internal-backend.patch deleted file mode 100644 index c353312ea7c..00000000000 --- a/patches/0003-Implement-crypto-internal-backend.patch +++ /dev/null @@ -1,3468 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: bot-for-go[bot] <199222863+bot-for-go[bot]@users.noreply.github.com> -Date: Wed, 15 Jan 2025 16:32:26 +0100 -Subject: [PATCH] Implement crypto/internal/backend - -Implement the supporting code for using the crypto backends, -but without using them yet. - -Update the Go toolchain build settings so that it uses the -desired goexperiments and build tags. ---- - .gitignore | 2 + - .../compile/internal/logopt/logopt_test.go | 5 + - src/cmd/compile/script_test.go | 8 + - src/cmd/dist/build.go | 80 +++- - src/cmd/dist/test.go | 18 +- - src/cmd/go/go_test.go | 11 + - src/cmd/go/internal/cfg/cfg.go | 16 + - src/cmd/go/internal/load/pkg.go | 3 + - src/cmd/go/internal/tool/tool.go | 9 +- - src/cmd/go/systemcrypto_test.go | 233 +++++++++ - .../go/testdata/script/env_cross_build.txt | 2 + - .../script/test_android_issue62123.txt | 2 + - src/cmd/internal/testdir/testdir_test.go | 7 + - src/cmd/link/internal/ld/main.go | 2 +- - src/cmd/link/link_test.go | 8 + - src/crypto/internal/backend/backend_darwin.go | 442 ++++++++++++++++++ - src/crypto/internal/backend/backend_linux.go | 435 +++++++++++++++++ - src/crypto/internal/backend/backend_test.go | 56 +++ - .../internal/backend/backend_windows.go | 442 ++++++++++++++++++ - src/crypto/internal/backend/bbig/big.go | 17 + - .../internal/backend/bbig/big_darwin.go | 12 + - src/crypto/internal/backend/bbig/big_linux.go | 12 + - .../internal/backend/bbig/big_windows.go | 12 + - src/crypto/internal/backend/common.go | 46 ++ - .../internal/backend/fips140/fips140.go | 95 ++++ - .../internal/backend/fips140/fips140_test.go | 75 +++ - .../internal/backend/fips140/isrequirefips.go | 9 + - .../internal/backend/fips140/norequirefips.go | 9 + - .../backend/fips140/nosystemcrypto.go | 13 + - .../fips140/requirefips_nosystemcrypto.go | 15 + - .../backend/fips140/skipfipscheck_off.go | 9 + - .../backend/fips140/skipfipscheck_on.go | 9 + - .../backend/fips140/systemfips_darwin.go | 13 + - .../backend/fips140/systemfips_linux.go | 59 +++ - .../backend/fips140/systemfips_windows.go | 35 ++ - .../opensslsetup/opensslsetup_linux.go | 68 +++ - .../opensslsetup/opensslsetup_linux_test.go | 92 ++++ - .../backend/internal/opensslsetup/stub.go | 8 + - src/crypto/internal/backend/nobackend.go | 376 +++++++++++++++ - src/crypto/internal/backend/stub.s | 10 + - .../internal/fips140only/fips140only.go | 11 +- - src/crypto/systemcrypto_nocgo_linux.go | 18 + - src/go/build/deps_test.go | 24 +- - src/internal/buildcfg/exp.go | 48 +- - src/runtime/runtime_boring.go | 5 + - 45 files changed, 2854 insertions(+), 27 deletions(-) - create mode 100644 src/cmd/go/systemcrypto_test.go - create mode 100644 src/crypto/internal/backend/backend_darwin.go - create mode 100644 src/crypto/internal/backend/backend_linux.go - create mode 100644 src/crypto/internal/backend/backend_test.go - create mode 100644 src/crypto/internal/backend/backend_windows.go - create mode 100644 src/crypto/internal/backend/bbig/big.go - create mode 100644 src/crypto/internal/backend/bbig/big_darwin.go - create mode 100644 src/crypto/internal/backend/bbig/big_linux.go - create mode 100644 src/crypto/internal/backend/bbig/big_windows.go - create mode 100644 src/crypto/internal/backend/common.go - create mode 100644 src/crypto/internal/backend/fips140/fips140.go - create mode 100644 src/crypto/internal/backend/fips140/fips140_test.go - create mode 100644 src/crypto/internal/backend/fips140/isrequirefips.go - create mode 100644 src/crypto/internal/backend/fips140/norequirefips.go - create mode 100644 src/crypto/internal/backend/fips140/nosystemcrypto.go - create mode 100644 src/crypto/internal/backend/fips140/requirefips_nosystemcrypto.go - create mode 100644 src/crypto/internal/backend/fips140/skipfipscheck_off.go - create mode 100644 src/crypto/internal/backend/fips140/skipfipscheck_on.go - create mode 100644 src/crypto/internal/backend/fips140/systemfips_darwin.go - create mode 100644 src/crypto/internal/backend/fips140/systemfips_linux.go - create mode 100644 src/crypto/internal/backend/fips140/systemfips_windows.go - create mode 100644 src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go - create mode 100644 src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go - create mode 100644 src/crypto/internal/backend/internal/opensslsetup/stub.go - create mode 100644 src/crypto/internal/backend/nobackend.go - create mode 100644 src/crypto/internal/backend/stub.s - create mode 100644 src/crypto/systemcrypto_nocgo_linux.go - -diff --git a/.gitignore b/.gitignore -index 344b31f7ac1c1d..b4583113d5aa15 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -47,6 +47,8 @@ _testmain.go - /test/run.out - /test/times.out - -+!/src/vendor/github.com/microsoft/go-crypto-darwin/internal/cryptokit/CryptoKit.o -+ - # This file includes artifacts of Go build that should not be checked in. - # For files created by specific development environment (e.g. editor), - # use alternative ways to exclude files from git. -diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go -index 1edabf9fb7ff04..f47c17401e480b 100644 ---- a/src/cmd/compile/internal/logopt/logopt_test.go -+++ b/src/cmd/compile/internal/logopt/logopt_test.go -@@ -5,6 +5,7 @@ - package logopt - - import ( -+ "internal/goexperiment" - "internal/testenv" - "os" - "path/filepath" -@@ -148,6 +149,10 @@ func s15a8(x *[15]int64) [15]int64 { - goos0 := runtime.GOOS - if runtime.GOARCH == "amd64" { // Test many things with "linux" (wasm will get "js") - arches = []string{"arm", "arm64", "386", "amd64", "mips", "mips64", "loong64", "ppc64le", "riscv64", "s390x", "wasm"} -+ if goexperiment.SystemCrypto { -+ // SystemCrypto does not support cross-compilation on all architectures. -+ arches = []string{"arm", "arm64", "386", "amd64", "loong64", "ppc64le", "riscv64", "s390x"} -+ } - goos0 = "linux" - } - -diff --git a/src/cmd/compile/script_test.go b/src/cmd/compile/script_test.go -index 0e32e0769ee2e2..eda2d576acc6c6 100644 ---- a/src/cmd/compile/script_test.go -+++ b/src/cmd/compile/script_test.go -@@ -7,6 +7,7 @@ package main - import ( - "cmd/internal/script/scripttest" - "flag" -+ "internal/goexperiment" - "internal/testenv" - "os" - "runtime" -@@ -50,6 +51,13 @@ func TestScript(t *testing.T) { - // the installed linker with our test binary. - doReplacement = false - } -+ if goexperiment.SystemCrypto { -+ // If systemcrypto is enabled, we can't replace the compiler -+ // because the test binary is built with systemcrypto, but the -+ // compiler is not. This would cause a mismatch in the -+ // compiler version check in the script tests. -+ doReplacement = false -+ } - repls := []scripttest.ToolReplacement{} - if doReplacement { - if testCompiler == "" { -diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go -index de2c7be2e833f9..415f4baeff2510 100644 ---- a/src/cmd/dist/build.go -+++ b/src/cmd/dist/build.go -@@ -1398,6 +1398,80 @@ func toolenv() []string { - // still have full paths for stack traces for compiler crashes and the like. - env = append(env, "GOFLAGS=-trimpath -ldflags=-w -gcflags=cmd/...=-dwarf=false") - } -+ return toolenvMicrosoft(env) -+} -+ -+// updateEnv updates the environment variable named name in env -+// by applying the function fn to its value. -+// If the variable is not present in env, the value is taken from -+// the current environment (os.Getenv), and the updated value is appended -+// to env. -+// If the variable is not present in the current environment, the new value -+// is appended to env. -+func updateEnv(name string, env []string, fn func(string) string) []string { -+ // Find the variable in env. -+ for i, e := range env { -+ if value, ok := strings.CutPrefix(e, name+"="); ok { -+ // Found it, update it. -+ env[i] = name + "=" + fn(value) -+ return env -+ } -+ } -+ -+ // Not found, use the current environment. -+ val := os.Getenv(name) -+ if val == "" { -+ val = fn("") -+ } else { -+ val = fn(val) -+ } -+ return append(env, name+"="+val) -+} -+ -+// toolenvMicrosoft appends Microsoft-specific environment variables to env. -+func toolenvMicrosoft(env []string) []string { -+ // Keep all the experiments from the current -+ // environment, but with "systemcrypto" removed and -+ // "nosystemcrypto" added. This is used when building -+ // the Microsoft build of Go to favor portability. -+ // Go only uses crypto packages for non-crypto purposes, -+ // such as generating build IDs. -+ env = updateEnv("GOEXPERIMENT", env, func(value string) string { -+ if value == "" { -+ return "nosystemcrypto" -+ } -+ goexp := strings.Split(value, ",") -+ goexp = slices.DeleteFunc(goexp, func(s string) bool { -+ return s == "systemcrypto" || s == "nosystemcrypto" -+ }) -+ goexp = append(goexp, "nosystemcrypto") -+ return strings.Join(goexp, ",") -+ }) -+ -+ // Build the Go toolchain with "-tags=ms_skipfipscheck". This -+ // allows toolchains not built with the systemcrypto goexperiment to be used -+ // when GODEBUG=fips140=on is set. For example, when running -+ // "GODEBUG=fips140=on go test ./..." or "GODEBUG=fips140=on go run .". -+ env = updateEnv("GOFLAGS", env, func(value string) string { -+ if value == "" { -+ return "-tags=ms_skipfipscheck" -+ } -+ goflags := strings.Fields(value) -+ // If -tags= is already set, append "ms_skipfipscheck" to it. -+ // If not, add it as a new flag. -+ var found bool -+ for i, e := range goflags { -+ if value, ok := strings.CutPrefix(e, "-tags="); ok { -+ goflags[i] = "-tags=" + value + ",ms_skipfipscheck" -+ found = true -+ break -+ } -+ } -+ if !found { -+ goflags = append(goflags, "-tags=ms_skipfipscheck") -+ } -+ return strings.Join(goflags, " ") -+ }) - return env - } - -@@ -1640,12 +1714,12 @@ func cmdbootstrap() { - os.Setenv("CC", compilerEnvLookup("CC", defaultcc, goos, goarch)) - xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch) - } -- goInstall(nil, goBootstrap, "std") -+ goInstall(toolenvMicrosoft(nil), goBootstrap, "std") - goInstall(toolenv(), goBootstrap, toolsToInstall...) - checkNotStale(toolenv(), goBootstrap, toolchain...) -- checkNotStale(nil, goBootstrap, "std") -+ checkNotStale(toolenvMicrosoft(nil), goBootstrap, "std") - checkNotStale(toolenv(), goBootstrap, toolsToInstall...) -- checkNotStale(nil, gorootBinGo, "std") -+ checkNotStale(toolenvMicrosoft(nil), gorootBinGo, "std") - checkNotStale(toolenv(), gorootBinGo, toolsToInstall...) - if debug { - run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") -diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go -index aadb1ff52b067f..dab28b6f1ced0a 100644 ---- a/src/cmd/dist/test.go -+++ b/src/cmd/dist/test.go -@@ -158,10 +158,12 @@ func (t *tester) run() { - } - } - -+ tags := []string{"-tags=ms_skipfipscheck"} -+ - if t.rebuild { - t.out("Building packages and commands.") - // Force rebuild the whole toolchain. -- goInstall(toolenv(), gorootBinGo, append([]string{"-a"}, toolchain...)...) -+ goInstall(toolenv(), gorootBinGo, append([]string{"-a"}, append(tags, toolchain...)...)...) - } - - if !t.listMode { -@@ -178,9 +180,9 @@ func (t *tester) run() { - // and virtualization we usually start with a clean GOCACHE, so we would - // end up rebuilding large parts of the standard library that aren't - // otherwise relevant to the actual set of packages under test. -- goInstall(toolenv(), gorootBinGo, toolchain...) -- goInstall(toolenv(), gorootBinGo, toolchain...) -- goInstall(toolenv(), gorootBinGo, toolsToInstall...) -+ goInstall(toolenv(), gorootBinGo, append(tags, toolchain...)...) -+ goInstall(toolenv(), gorootBinGo, append(tags, toolchain...)...) -+ goInstall(toolenv(), gorootBinGo, append(tags, "cmd")...) - } - } - -@@ -774,7 +776,9 @@ func (t *tester) registerTests() { - } - - // Test ios/amd64 for the iOS simulator. -- if goos == "darwin" && goarch == "amd64" && t.cgoEnabled { -+ if goos == "darwin" && goarch == "amd64" && t.cgoEnabled && -+ // IOS is not supported with systemcrypto. -+ !strings.Contains(goexperiment, "systemcrypto") { - t.registerTest("GOOS=ios on darwin/amd64", - &goTest{ - variant: "amd64ios", -@@ -1023,7 +1027,9 @@ func (t *tester) registerTests() { - t.registerRaceTests() - } - -- if goos != "android" && !t.iOS() { -+ // cmd/internal/testdir uses -buildmode=exe on darwin, -+ // which is not supported by systemcrypto. -+ if goos != "android" && !t.iOS() && !strings.Contains(goexperiment, "systemcrypto") { - // Only start multiple test dir shards on builders, - // where they get distributed to multiple machines. - // See issues 20141 and 31834. -diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go -index 47839e0229b951..2d1a7fa99d551e 100644 ---- a/src/cmd/go/go_test.go -+++ b/src/cmd/go/go_test.go -@@ -13,6 +13,7 @@ import ( - "fmt" - "go/format" - "internal/godebug" -+ "internal/goexperiment" - "internal/platform" - "internal/testenv" - "io" -@@ -110,6 +111,13 @@ func TestMain(m *testing.M) { - if v := os.Getenv("TESTGO_TOOLCHAIN_VERSION"); v != "" { - work.ToolchainVersion = v - } -+ // Remove the " X:systemcrypto" suffix from the toolchain version, -+ // else it won't match the compiler version and the tests will fail. -+ // This happens because "go" invocations inside TestScript don't -+ // really call the go binary, which is never built with systemcrypto, -+ // but it instead calls the test binary with CMDGO_TEST_RUN_MAIN=1, -+ // which can be built with systemcrypto. -+ work.ToolchainVersion = strings.ReplaceAll(work.ToolchainVersion, " X:systemcrypto", "") - - if testGOROOT := os.Getenv("TESTGO_GOROOT"); testGOROOT != "" { - // Disallow installs to the GOROOT from which testgo was built. -@@ -1779,6 +1787,9 @@ func TestGenerateUsesBuildContext(t *testing.T) { - } - - func TestGoEnv(t *testing.T) { -+ if goexperiment.SystemCrypto { -+ t.Skip("freebsd is not supported by system crypto") -+ } - tg := testgo(t) - tg.parallel() - defer tg.cleanup() -diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go -index a4edd854f1d35a..10ee92536b96cd 100644 ---- a/src/cmd/go/internal/cfg/cfg.go -+++ b/src/cmd/go/internal/cfg/cfg.go -@@ -19,6 +19,7 @@ import ( - "os" - "path/filepath" - "runtime" -+ "runtime/debug" - "strings" - "sync" - "time" -@@ -229,6 +230,21 @@ func ForceHost() { - BuildContext = defaultContext() - // Call SetGOROOT to properly set the GOROOT on the new context. - SetGOROOT(Getenv("GOROOT"), false) -+ // The Microsoft build of Go might be built with non-default -+ // GOEXPERIMENT and BuildTags, so we need to get them from -+ // the build info. -+ bi, ok := debug.ReadBuildInfo() -+ if !ok { -+ panic("missing build info") -+ } -+ for _, s := range bi.Settings { -+ switch s.Key { -+ case "-tags": -+ BuildContext.BuildTags = strings.Split(s.Value, ",") -+ case "GOEXPERIMENT": -+ RawGOEXPERIMENT = s.Value -+ } -+ } - // Recompute experiments: the settings determined depend on GOOS and GOARCH. - // This will also update the BuildContext's tool tags to include the new - // experiment tags. -diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go -index 1d90061b065f5f..0d5d4fe0556564 100644 ---- a/src/cmd/go/internal/load/pkg.go -+++ b/src/cmd/go/internal/load/pkg.go -@@ -2417,6 +2417,9 @@ func (p *Package) setBuildInfo(ctx context.Context, f *modfetch.Fetcher, autoVCS - buildmode = "archive" - } - } -+ if cfg.Experiment.SystemCrypto { -+ appendSetting("microsoft_systemcrypto", "1") -+ } - appendSetting("-buildmode", buildmode) - appendSetting("-compiler", cfg.BuildContext.Compiler) - if gccgoflags := BuildGccgoflags.String(); gccgoflags != "" && cfg.BuildContext.Compiler == "gccgo" { -diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go -index 094c5b719bb291..4d2ab9e00395a8 100644 ---- a/src/cmd/go/internal/tool/tool.go -+++ b/src/cmd/go/internal/tool/tool.go -@@ -335,6 +335,10 @@ func buildAndRunBuiltinTool(ld *modload.Loader, ctx context.Context, toolName, t - // Override GOOS and GOARCH for the build to build the tool using - // the same GOOS and GOARCH as this go command. - cfg.ForceHost() -+ // The tool might build some additional packages, set the GOEXPERIMENT -+ // environment variable to the one we just computed in cfg.ForceHost. -+ originalGOEXPERIMENT := os.Getenv("GOEXPERIMENT") -+ os.Setenv("GOEXPERIMENT", cfg.RawGOEXPERIMENT) - - // Ignore go.mod and go.work: we don't need them, and we want to be able - // to run the tool even if there's an issue with the module or workspace the -@@ -342,8 +346,11 @@ func buildAndRunBuiltinTool(ld *modload.Loader, ctx context.Context, toolName, t - ld.RootMode = modload.NoRoot - - runFunc := func(b *work.Builder, ctx context.Context, a *work.Action) error { -+ // Now that the tool is built, revert the GOEXPERIMENT -+ // environment variable to the original value. -+ env := append(os.Environ(), "GOEXPERIMENT="+originalGOEXPERIMENT) - cmdline := str.StringList(builtTool(a), a.Args) -- return runBuiltTool(toolName, nil, cmdline) -+ return runBuiltTool(toolName, env, cmdline) - } - - buildAndRunTool(ld, ctx, tool, args, runFunc) -diff --git a/src/cmd/go/systemcrypto_test.go b/src/cmd/go/systemcrypto_test.go -new file mode 100644 -index 00000000000000..d6cc434197cff1 ---- /dev/null -+++ b/src/cmd/go/systemcrypto_test.go -@@ -0,0 +1,233 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+package main_test -+ -+import ( -+ "fmt" -+ "internal/testenv" -+ "os" -+ "path/filepath" -+ "slices" -+ "strings" -+ "testing" -+) -+ -+const fileWithCrypto = ` -+package main -+ -+import ( -+ "crypto/sha256" -+ "fmt" -+) -+ -+func main() { -+ fmt.Println(sha256.Sum256([]byte("Hello, World!"))) -+} -+` -+ -+const fileWithoutCrypto = ` -+package main -+ -+import ( -+ "fmt" -+) -+ -+func main() { -+ fmt.Println("Hello, World!") -+} -+` -+ -+func execGoTool(t *testing.T, allowFail bool, env []string, args ...string) (string, bool) { -+ t.Helper() -+ cmd := testenv.Command(t, testenv.GoToolPath(t), args...) -+ cmd = testenv.CleanCmdEnv(cmd) -+ cmd.Env = append(cmd.Env, env...) -+ out, err := cmd.CombinedOutput() -+ sout := strings.TrimSpace(string(out)) -+ if err != nil { -+ if allowFail { -+ return sout, false -+ } -+ t.Fatalf("go build failed: %v\n%s", err, out) -+ } -+ return sout, true -+} -+ -+// writeFile creates a temporary file with the given content and returns its path. -+func writeFile(t *testing.T, content string) string { -+ t.Helper() -+ name := "main.go" -+ if strings.Contains(content, "testing") { -+ name = "main_test.go" -+ } -+ name = filepath.Join(t.TempDir(), name) -+ if err := os.WriteFile(name, []byte(content), 0o644); err != nil { -+ t.Fatal(err) -+ } -+ return name -+} -+ -+func outPath(t *testing.T) string { -+ t.Helper() -+ return filepath.Join(t.TempDir(), "out") -+} -+ -+func TestSystemCryptoNoCgoChecks(t *testing.T) { -+ t.Parallel() -+ -+ cryptoFile := writeFile(t, fileWithCrypto) -+ -+ tt := []struct { -+ goos string -+ goarch string -+ goexp string -+ succeed bool -+ }{ -+ {"linux", "386", "", true}, -+ {"linux", "amd64", "", true}, -+ {"linux", "arm64", "", true}, -+ {"linux", "loong64", "", true}, -+ {"linux", "ppc64le", "", true}, -+ {"linux", "riscv64", "", true}, -+ {"linux", "s390x", "", true}, -+ {"linux", "arm", "", true}, -+ {"darwin", "amd64", "", true}, -+ {"darwin", "arm64", "", true}, -+ {"windows", "386", "", false}, // cgoless system crypto not supported on windows/386 -+ {"windows", "amd64", "", true}, -+ {"windows", "arm64", "", true}, -+ } -+ for _, tc := range tt { -+ t.Run(tc.goos+"/"+tc.goarch+"/"+tc.goexp, func(t *testing.T) { -+ t.Parallel() -+ if tc.goexp == "" { -+ tc.goexp = "systemcrypto" -+ } else { -+ tc.goexp += ",systemcrypto" -+ } -+ env := []string{"CGO_ENABLED=0", "GOOS=" + tc.goos, "GOARCH=" + tc.goarch, "GOEXPERIMENT=" + tc.goexp, "MS_GO_NOSYSTEMCRYPTO=0"} -+ if out, ok := execGoTool(t, true, env, "build", "-o", outPath(t), cryptoFile); ok != tc.succeed { -+ if tc.succeed { -+ t.Fatalf("expected success, got failure: %s", out) -+ } else { -+ t.Fatalf("expected failure, got success") -+ } -+ } -+ }) -+ } -+} -+ -+func TestSystemCryptoFIPS(t *testing.T) { -+ // Test different go commands with GODEBUG=fips140=on -+ // to exercise the ms_skipfipscheck build tag. -+ t.Parallel() -+ env := []string{"GODEBUG=fips140=on"} -+ -+ cryptoFile := writeFile(t, fileWithCrypto) -+ nonCryptoFile := writeFile(t, fileWithoutCrypto) -+ -+ // Build should always succeed given that the go toolchain -+ // is built with the ms_skipfipscheck build tag. -+ execGoTool(t, false, env, "build", "-o", outPath(t), cryptoFile) -+ execGoTool(t, false, env, "build", "-o", outPath(t), nonCryptoFile) -+ -+ // Run should always succeed if the target go package -+ // doesn't use crypto. -+ execGoTool(t, false, env, "run", nonCryptoFile) -+ -+ // Run may or may not fail if the target go package uses crypto, -+ // because while the toolchain was built with ms_skipfipscheck, the -+ // target program was not. Failure depends on system crypto mode -+ // and presence of system-provided crypto, and it can't be tested here. -+} -+ -+func TestSystemCryptoDefault(t *testing.T) { -+ t.Parallel() -+ cryptoFile := writeFile(t, fileWithCrypto) -+ -+ // Add here all the OS/ARCH combinations that enable systemcrypto by default. -+ type testCase struct { -+ goos string -+ goarch string -+ } -+ test := []testCase{ -+ {"linux", "amd64"}, -+ {"linux", "arm64"}, -+ {"darwin", "amd64"}, -+ {"darwin", "arm64"}, -+ {"windows", "amd64"}, -+ {"windows", "arm64"}, -+ } -+ for _, tt := range test { -+ t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), func(t *testing.T) { -+ t.Parallel() -+ out := outPath(t) -+ env := []string{"CGO_ENABLED=0", "GOOS=" + tt.goos, "GOARCH=" + tt.goarch, "MS_GO_NOSYSTEMCRYPTO=0"} -+ // Set a goexperiment in case the Go toolchain has been built with GOEXPERIMENT=nosystemcrypto. -+ // In that case, systemcrypto will not be enabled by default unless we pass an explicit GOEXPERIMENT. -+ env = append(env, "GOEXPERIMENT=regabi") -+ execGoTool(t, false, env, "build", "-o", out, cryptoFile) -+ // Check that the binary has the correct settings. -+ settings, _ := execGoTool(t, false, nil, "version", "-m", out) -+ if !strings.Contains(settings, "microsoft_systemcrypto=1") { -+ t.Errorf("expected microsoft_systemcrypto=1 in settings, got %v", settings) -+ } -+ }) -+ } -+ -+} -+ -+func TestSystemCryptoSetting(t *testing.T) { -+ t.Parallel() -+ file := writeFile(t, fileWithCrypto) -+ -+ type testCase struct { -+ enabled bool -+ env []string -+ } -+ tests := []testCase{ -+ {false, []string{"GOEXPERIMENT=nosystemcrypto"}}, -+ {false, []string{"GOEXPERIMENT=systemcrypto,nosystemcrypto"}}, -+ {false, []string{"MS_GO_NOSYSTEMCRYPTO=1"}}, -+ {true, []string{"GOEXPERIMENT=systemcrypto"}}, -+ {true, []string{"GOEXPERIMENT=nosystemcrypto,systemcrypto"}}, -+ } -+ // Duplicate each test with MS_GO_NOSYSTEMCRYPTO=1, which should always result -+ // in systemcrypto being disabled. -+ n := len(tests) -+ for i := range n { -+ tt := tests[i] -+ tests = append(tests, testCase{false, append(tt.env, "MS_GO_NOSYSTEMCRYPTO=1")}) -+ } -+ for _, tt := range tests { -+ t.Run(strings.Join(tt.env, ","), func(t *testing.T) { -+ t.Parallel() -+ out := outPath(t) -+ -+ if !slices.ContainsFunc(tt.env, func(r string) bool { -+ return strings.HasPrefix(r, "MS_GO_NOSYSTEMCRYPTO=") -+ }) { -+ // Ensure MS_GO_NOSYSTEMCRYPTO is set to 0 if not specified -+ // to avoid interference from the environment. -+ tt.env = append(tt.env, "MS_GO_NOSYSTEMCRYPTO=0") -+ } -+ -+ // Build the binary with the specified GOEXPERIMENT. -+ _, ok := execGoTool(t, true, tt.env, "build", "-o", out, file) -+ if !ok { -+ t.Skip("skipping test because GOEXPERIMENT not supported by the current toolchain") -+ } -+ -+ // Check that the binary has the correct settings. -+ settings, _ := execGoTool(t, false, nil, "version", "-m", out) -+ hasSetting := strings.Contains(settings, "microsoft_systemcrypto=1") -+ if tt.enabled && !hasSetting { -+ t.Errorf("expected microsoft_systemcrypto=1 in settings, got %v", settings) -+ } else if !tt.enabled && hasSetting { -+ t.Errorf("expected microsoft_systemcrypto=1 not to be in settings, got %v", settings) -+ } -+ }) -+ } -+} -diff --git a/src/cmd/go/testdata/script/env_cross_build.txt b/src/cmd/go/testdata/script/env_cross_build.txt -index 91d1cb936d3652..b7f503d9506324 100644 ---- a/src/cmd/go/testdata/script/env_cross_build.txt -+++ b/src/cmd/go/testdata/script/env_cross_build.txt -@@ -1,6 +1,8 @@ - # Test that the correct default GOEXPERIMENT is used when cross - # building with GOENV (#46815). - -+[GOEXPERIMENT:systemcrypto] skip # systemcrypto is not supported on ios nor mips -+ - # Unset variables set by the TestScript harness. Users typically won't - # explicitly configure these, and #46815 doesn't repro if they are. - env GOOS= -diff --git a/src/cmd/go/testdata/script/test_android_issue62123.txt b/src/cmd/go/testdata/script/test_android_issue62123.txt -index 2f46a6b44bffe1..69071289e2de81 100644 ---- a/src/cmd/go/testdata/script/test_android_issue62123.txt -+++ b/src/cmd/go/testdata/script/test_android_issue62123.txt -@@ -1,3 +1,5 @@ -+[GOEXPERIMENT:systemcrypto] skip # systemcrypto is not supported on android -+ - env GOOS=android GOARCH=amd64 CGO_ENABLED=0 - - ! go build -o $devnull cmd/buildid -diff --git a/src/cmd/internal/testdir/testdir_test.go b/src/cmd/internal/testdir/testdir_test.go -index 07984396b7d44e..c7fbda270159f3 100644 ---- a/src/cmd/internal/testdir/testdir_test.go -+++ b/src/cmd/internal/testdir/testdir_test.go -@@ -118,6 +118,13 @@ func Test(t *testing.T) { - goDebug = env.GODEBUG - tmpDir = t.TempDir() - -+ if slices.ContainsFunc(strings.Split(goExperiment, ","), func(s string) bool { -+ return s == "systemcrypto" -+ }) { -+ // Codegen tests cross-compile to platforms that do not support system crypto. -+ *allCodegen = false -+ } -+ - common := testCommon{ - gorootTestDir: filepath.Join(testenv.GOROOT(t), "test"), - runoutputGate: make(chan bool, *runoutputLimit), -diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go -index 19ed197a20b57e..5e592c7c6697d7 100644 ---- a/src/cmd/link/internal/ld/main.go -+++ b/src/cmd/link/internal/ld/main.go -@@ -189,7 +189,7 @@ func Main(arch *sys.Arch, theArch Arch) { - } - - buildVersion := buildcfg.Version -- if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { -+ if goexperiment := buildcfg.Experiment.MSString(); goexperiment != "" { - sep := " " - if !strings.Contains(buildVersion, "-") { // See go.dev/issue/75953. - sep = "-" -diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go -index e7c406c4ad1eb8..ac36a6dd1ca1a0 100644 ---- a/src/cmd/link/link_test.go -+++ b/src/cmd/link/link_test.go -@@ -13,6 +13,7 @@ import ( - "errors" - "internal/abi" - "internal/buildcfg" -+ "internal/goexperiment" - "internal/platform" - "internal/testenv" - "internal/xcoff" -@@ -368,6 +369,9 @@ func TestBuildForTvOS(t *testing.T) { - if runtime.GOOS != "darwin" { - t.Skip("skipping on non-darwin platform") - } -+ if goexperiment.SystemCrypto { -+ t.Skip("tvOS is not supported by system crypto") -+ } - if testing.Short() && testenv.Builder() == "" { - t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty") - } -@@ -598,6 +602,10 @@ func TestIssue34788Android386TLSSequence(t *testing.T) { - t.Skip("skipping on non-{linux,darwin}/amd64 platform") - } - -+ if goexperiment.SystemCrypto { -+ t.Skip("skipping on system crypto, which does not support android/386") -+ } -+ - t.Parallel() - - tmpdir := t.TempDir() -diff --git a/src/crypto/internal/backend/backend_darwin.go b/src/crypto/internal/backend/backend_darwin.go -new file mode 100644 -index 00000000000000..73830cae246cbd ---- /dev/null -+++ b/src/crypto/internal/backend/backend_darwin.go -@@ -0,0 +1,442 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+// Package darwin provides access to DarwinCrypto implementation functions. -+// Check the variable Enabled to find out whether DarwinCrypto is available. -+// If DarwinCrypto is not available, the functions in this package all panic. -+package backend -+ -+import ( -+ "crypto" -+ "crypto/cipher" -+ "crypto/internal/backend/fips140" -+ "crypto/internal/boring/sig" -+ "crypto/internal/fips140only" -+ "errors" -+ "hash" -+ "io" -+ _ "unsafe" -+ -+ "github.com/microsoft/go-crypto-darwin/xcrypto" -+) -+ -+func init() { -+ // Darwin is considered FIPS compliant. -+ if err := fips140.Check(func() bool { return true }); err != nil { -+ panic("darwincrypto: " + err.Error()) -+ } -+ sig.BoringCrypto() -+ fips140only.BackendApprovedHash = FIPSApprovedHash -+} -+ -+// Enabled controls whether FIPS crypto is enabled. -+const Enabled = true -+ -+type BigInt = xcrypto.BigInt -+ -+const RandReader = xcrypto.RandReader -+ -+func SupportsHash(h crypto.Hash) bool { -+ return xcrypto.SupportsHash(h) -+} -+ -+func FIPSApprovedHash(h hash.Hash) bool { -+ return xcrypto.FIPSApprovedHash(h) -+} -+ -+func SupportsSHAKE(securityBits int) bool { return false } -+func SupportsCSHAKE(securityBits int) bool { return false } -+ -+func SupportsCurve(curve string) bool { -+ switch curve { -+ case "P-256", "P-384", "P-521", "X25519": -+ return true -+ } -+ return false -+} -+ -+func SupportsRSAOAEPLabel(label []byte) bool { -+ // CommonCrypto doesn't support labels -+ // https://github.com/microsoft/go-crypto-darwin/issues/22 -+ return len(label) == 0 -+} -+ -+func SupportsRSAPKCS1v15Encryption() bool { return true } -+ -+func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { -+ switch hash { -+ case crypto.SHA1, crypto.SHA224, crypto.SHA256, crypto.SHA384, crypto.SHA512, 0: -+ return true -+ } -+ return false -+} -+ -+type Hash = xcrypto.Hash -+ -+type SHAKE struct { -+ io.Reader -+ hash.Hash -+} -+ -+func (s *SHAKE) MarshalBinary() ([]byte, error) { panic("cryptobackend: not available") } -+func (s *SHAKE) AppendBinary(p []byte) ([]byte, error) { panic("cryptobackend: not available") } -+func (s *SHAKE) UnmarshalBinary(data []byte) error { panic("cryptobackend: not available") } -+ -+func NewMD5() hash.Hash { return xcrypto.NewMD5() } -+func NewSHA1() hash.Hash { return xcrypto.NewSHA1() } -+func NewSHA224() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA256() hash.Hash { return xcrypto.NewSHA256() } -+func NewSHA384() hash.Hash { return xcrypto.NewSHA384() } -+func NewSHA512() hash.Hash { return xcrypto.NewSHA512() } -+func NewSHA512_224() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA512_256() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA3_224() *Hash { panic("cryptobackend: not available") } -+func NewSHA3_256() *Hash { return xcrypto.NewSHA3_256() } -+func NewSHA3_384() *Hash { return xcrypto.NewSHA3_384() } -+func NewSHA3_512() *Hash { return xcrypto.NewSHA3_512() } -+ -+func NewSHAKE128() *SHAKE { panic("cryptobackend: not available") } -+func NewSHAKE256() *SHAKE { panic("cryptobackend: not available") } -+func NewCSHAKE128(N, S []byte) *SHAKE { panic("cryptobackend: not available") } -+func NewCSHAKE256(N, S []byte) *SHAKE { panic("cryptobackend: not available") } -+ -+func MD5(p []byte) (sum [16]byte) { return xcrypto.MD5(p) } -+func SHA1(p []byte) (sum [20]byte) { return xcrypto.SHA1(p) } -+func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SHA256(p []byte) (sum [32]byte) { return xcrypto.SHA256(p) } -+func SHA384(p []byte) (sum [48]byte) { return xcrypto.SHA384(p) } -+func SHA512(p []byte) (sum [64]byte) { return xcrypto.SHA512(p) } -+func SHA512_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SHA512_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } -+func SumSHA3_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SumSHA3_256(p []byte) (sum [32]byte) { return xcrypto.SumSHA3_256(p) } -+func SumSHA3_384(p []byte) (sum [48]byte) { return xcrypto.SumSHA3_384(p) } -+func SumSHA3_512(p []byte) (sum [64]byte) { return xcrypto.SumSHA3_512(p) } -+ -+func SumSHAKE128(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } -+func SumSHAKE256(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } -+ -+func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { -+ return xcrypto.NewHMAC(h, key) -+} -+ -+func NewAESCipher(key []byte) (cipher.Block, error) { -+ return xcrypto.NewAESCipher(key) -+} -+ -+func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { -+ return xcrypto.NewGCMTLS(c) -+} -+ -+func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { -+ return xcrypto.NewGCMTLS13(c) -+} -+ -+type PublicKeyECDSA = xcrypto.PublicKeyECDSA -+type PrivateKeyECDSA = xcrypto.PrivateKeyECDSA -+ -+func GenerateKeyECDSA(curve string) (X, Y, D xcrypto.BigInt, err error) { -+ return xcrypto.GenerateKeyECDSA(curve) -+} -+ -+func NewPrivateKeyECDSA(curve string, X, Y, D xcrypto.BigInt) (*xcrypto.PrivateKeyECDSA, error) { -+ return xcrypto.NewPrivateKeyECDSA(curve, X, Y, D) -+} -+ -+func NewPublicKeyECDSA(curve string, X, Y xcrypto.BigInt) (*xcrypto.PublicKeyECDSA, error) { -+ return xcrypto.NewPublicKeyECDSA(curve, X, Y) -+} -+ -+//go:linkname encodeSignature crypto/ecdsa.encodeSignature -+func encodeSignature(r, s []byte) ([]byte, error) -+ -+//go:linkname parseSignature crypto/ecdsa.parseSignature -+func parseSignature(sig []byte) (r, s []byte, err error) -+ -+func SignMarshalECDSA(priv *xcrypto.PrivateKeyECDSA, hash []byte) ([]byte, error) { -+ return xcrypto.SignMarshalECDSA(priv, hash) -+} -+ -+func VerifyECDSA(pub *xcrypto.PublicKeyECDSA, hash []byte, sig []byte) bool { -+ return xcrypto.VerifyECDSA(pub, hash, sig) -+} -+ -+func SupportsRSAPrivateKey(bits, primes int) bool { -+ return primes == 2 && SupportsRSAPublicKey(bits) -+} -+ -+func SupportsRSAPublicKey(bits int) bool { -+ return bits >= 1024 && bits%8 == 0 && bits <= 16384 -+} -+ -+func SupportsRSASaltLength(sign bool, salt int) bool { -+ // CommonCrypto doesn't support custom salt length -+ return salt == -1 -+} -+ -+type PublicKeyRSA = xcrypto.PublicKeyRSA -+type PrivateKeyRSA = xcrypto.PrivateKeyRSA -+ -+func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *xcrypto.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { -+ return xcrypto.DecryptRSAOAEP(h, priv, ciphertext, label) -+} -+ -+func DecryptRSAPKCS1(priv *xcrypto.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ return xcrypto.DecryptRSAPKCS1(priv, ciphertext) -+} -+ -+func DecryptRSANoPadding(priv *xcrypto.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ return xcrypto.DecryptRSANoPadding(priv, ciphertext) -+} -+ -+func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *xcrypto.PublicKeyRSA, msg, label []byte) ([]byte, error) { -+ return xcrypto.EncryptRSAOAEP(h, pub, msg, label) -+} -+ -+func EncryptRSAPKCS1(pub *xcrypto.PublicKeyRSA, msg []byte) ([]byte, error) { -+ return xcrypto.EncryptRSAPKCS1(pub, msg) -+} -+ -+func EncryptRSANoPadding(pub *xcrypto.PublicKeyRSA, msg []byte) ([]byte, error) { -+ return xcrypto.EncryptRSANoPadding(pub, msg) -+} -+ -+//go:linkname decodeKeyRSA crypto/rsa.decodeKey -+func decodeKeyRSA(data []byte) (N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt, err error) -+ -+//go:linkname encodeKeyRSA crypto/rsa.encodeKey -+func encodeKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt) ([]byte, error) -+ -+//go:linkname encodePublicKeyRSA crypto/rsa.encodePublicKey -+func encodePublicKeyRSA(N, E xcrypto.BigInt) ([]byte, error) -+ -+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt, err error) { -+ data, err := xcrypto.GenerateKeyRSA(bits) -+ if err != nil { -+ return -+ } -+ return decodeKeyRSA(data) -+} -+ -+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv xcrypto.BigInt) (*xcrypto.PrivateKeyRSA, error) { -+ encoded, err := encodeKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) -+ if err != nil { -+ return nil, err -+ } -+ return xcrypto.NewPrivateKeyRSA(encoded) -+} -+ -+func NewPublicKeyRSA(N, E xcrypto.BigInt) (*xcrypto.PublicKeyRSA, error) { -+ encoded, err := encodePublicKeyRSA(N, E) -+ if err != nil { -+ return nil, err -+ } -+ return xcrypto.NewPublicKeyRSA(encoded) -+} -+ -+func SignRSAPKCS1v15(priv *xcrypto.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { -+ return xcrypto.SignRSAPKCS1v15(priv, h, hashed) -+} -+ -+func SignRSAPSS(priv *xcrypto.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { -+ return xcrypto.SignRSAPSS(priv, h, hashed, saltLen) -+} -+ -+func VerifyRSAPKCS1v15(pub *xcrypto.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { -+ return xcrypto.VerifyRSAPKCS1v15(pub, h, hashed, sig) -+} -+ -+func VerifyRSAPSS(pub *xcrypto.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { -+ return xcrypto.VerifyRSAPSS(pub, h, hashed, sig, saltLen) -+} -+ -+type PrivateKeyECDH = xcrypto.PrivateKeyECDH -+type PublicKeyECDH = xcrypto.PublicKeyECDH -+ -+func ECDH(priv *xcrypto.PrivateKeyECDH, pub *xcrypto.PublicKeyECDH) ([]byte, error) { -+ return xcrypto.ECDH(priv, pub) -+} -+ -+func GenerateKeyECDH(curve string) (*xcrypto.PrivateKeyECDH, []byte, error) { -+ return xcrypto.GenerateKeyECDH(curve) -+} -+ -+func NewPrivateKeyECDH(curve string, bytes []byte) (*xcrypto.PrivateKeyECDH, error) { -+ return xcrypto.NewPrivateKeyECDH(curve, bytes) -+} -+ -+func NewPublicKeyECDH(curve string, bytes []byte) (*xcrypto.PublicKeyECDH, error) { -+ return xcrypto.NewPublicKeyECDH(curve, bytes) -+} -+ -+func SupportsTLS13KDF() bool { -+ return false -+} -+ -+func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsHKDF() bool { -+ return true -+} -+ -+func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { -+ return xcrypto.ExpandHKDF(h, pseudorandomKey, info, keyLength) -+} -+ -+func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { -+ return xcrypto.ExtractHKDF(h, secret, salt) -+} -+ -+func SupportsPBKDF2() bool { -+ return true -+} -+ -+func PBKDF2(pass, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) { -+ return xcrypto.PBKDF2(pass, salt, iter, keyLen, h) -+} -+ -+func SupportsTLS1PRF() bool { -+ return false -+} -+ -+func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsDESCipher() bool { -+ return true -+} -+ -+func SupportsTripleDESCipher() bool { -+ return true -+} -+ -+func NewDESCipher(key []byte) (cipher.Block, error) { -+ return xcrypto.NewDESCipher(key) -+} -+ -+func NewTripleDESCipher(key []byte) (cipher.Block, error) { -+ return xcrypto.NewTripleDESCipher(key) -+} -+ -+func SupportsRC4() bool { return true } -+ -+type RC4Cipher = xcrypto.RC4Cipher -+ -+func NewRC4Cipher(key []byte) (*RC4Cipher, error) { return xcrypto.NewRC4Cipher(key) } -+ -+func SupportsEd25519() bool { -+ return true -+} -+ -+type PublicKeyEd25519 = xcrypto.PublicKeyEd25519 -+type PrivateKeyEd25519 = xcrypto.PrivateKeyEd25519 -+ -+func GenerateKeyEd25519() (PrivateKeyEd25519, error) { -+ return xcrypto.GenerateKeyEd25519(), nil -+} -+ -+func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { -+ return xcrypto.NewPrivateKeyEd25519(priv) -+} -+ -+func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { -+ return xcrypto.NewPublicKeyEd25519(pub) -+} -+ -+func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { -+ return xcrypto.NewPrivateKeyEd25519FromSeed(seed) -+} -+ -+func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { -+ return xcrypto.SignEd25519(priv, message) -+} -+ -+func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { -+ return xcrypto.VerifyEd25519(pub, message, sig) -+} -+ -+func SupportsDSA(l, n int) bool { -+ return false -+} -+ -+func GenerateParametersDSA(l, n int) (p, q, g xcrypto.BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+ -+type PrivateKeyDSA struct{} -+type PublicKeyDSA struct{} -+ -+func GenerateKeyDSA(p, q, g xcrypto.BigInt) (x, y xcrypto.BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPrivateKeyDSA(p, q, g, x, y xcrypto.BigInt) (*PrivateKeyDSA, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPublicKeyDSA(p, q, g, y xcrypto.BigInt) (*PublicKeyDSA, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (xcrypto.BigInt, xcrypto.BigInt, error)) (r, s xcrypto.BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+ -+func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s xcrypto.BigInt, encodeSignature func(r, s xcrypto.BigInt) ([]byte, error)) bool { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsMLKEM768() bool { -+ return xcrypto.SupportsMLKEM() -+} -+ -+func SupportsMLKEM1024() bool { -+ return xcrypto.SupportsMLKEM() -+} -+ -+type DecapsulationKeyMLKEM768 = xcrypto.DecapsulationKeyMLKEM768 -+type EncapsulationKeyMLKEM768 = xcrypto.EncapsulationKeyMLKEM768 -+ -+func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { -+ return xcrypto.GenerateKeyMLKEM768() -+} -+ -+func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { -+ return xcrypto.NewDecapsulationKeyMLKEM768(seed) -+} -+ -+func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { -+ return xcrypto.NewEncapsulationKeyMLKEM768(encapsulationKey) -+} -+ -+type DecapsulationKeyMLKEM1024 = xcrypto.DecapsulationKeyMLKEM1024 -+type EncapsulationKeyMLKEM1024 = xcrypto.EncapsulationKeyMLKEM1024 -+ -+func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { -+ return xcrypto.GenerateKeyMLKEM1024() -+} -+ -+func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { -+ return xcrypto.NewDecapsulationKeyMLKEM1024(seed) -+} -+ -+func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { -+ return xcrypto.NewEncapsulationKeyMLKEM1024(encapsulationKey) -+} -+ -+func SupportsChaCha20Poly1305() bool { -+ return true -+} -+ -+func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { -+ if fips140only.Enforced() { -+ return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode") -+ } -+ return xcrypto.NewChaCha20Poly1305(key) -+} -diff --git a/src/crypto/internal/backend/backend_linux.go b/src/crypto/internal/backend/backend_linux.go -new file mode 100644 -index 00000000000000..a28f644c950f86 ---- /dev/null -+++ b/src/crypto/internal/backend/backend_linux.go -@@ -0,0 +1,435 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+// Package openssl provides access to OpenSSLCrypto implementation functions. -+// Check the variable Enabled to find out whether OpenSSLCrypto is available. -+// If OpenSSLCrypto is not available, the functions in this package all panic. -+package backend -+ -+import ( -+ "crypto" -+ "crypto/cipher" -+ "crypto/internal/backend/fips140" -+ _ "crypto/internal/backend/internal/opensslsetup" -+ "crypto/internal/boring/sig" -+ "crypto/internal/fips140only" -+ "errors" -+ "hash" -+ -+ "github.com/microsoft/go-crypto-openssl/openssl" -+ "github.com/microsoft/go-crypto-openssl/osslsetup" -+) -+ -+// Enabled controls whether FIPS crypto is enabled. -+const Enabled = true -+ -+type BigInt = openssl.BigInt -+ -+func init() { -+ // Some distributions, e.g. Azure Linux 3, don't set the `fips=yes` property when running in FIPS mode, -+ // but they configure OpenSSL to use a FIPS-compliant provider (in the case of Azure Linux 3, the SCOSSL provider). -+ // In this cases, openssl.FIPS would return `false` and openssl.FIPSCapable would return `true`. -+ // We don't care about the `fips=yes` property as long as the provider is FIPS-compliant, so use -+ // osslsetup.FIPS to determine whether FIPS mode is enabled. -+ if err := fips140.Check(func() bool { return osslsetup.FIPS() }); err != nil { -+ // This path can be reached for the following reasons: -+ // - In OpenSSL 1, the active engine doesn't support FIPS mode. -+ // - In OpenSSL 1, the active engine supports FIPS mode, but it is not enabled. -+ // - In OpenSSL 3, the provider used by default doesn't match the `fips=yes` query. -+ panic("opensslcrypto: " + err.Error() + ": " + osslsetup.VersionText()) -+ } -+ sig.BoringCrypto() -+ fips140only.BackendApprovedHash = FIPSApprovedHash -+} -+ -+const RandReader = openssl.RandReader -+ -+func SupportsHash(h crypto.Hash) bool { -+ return openssl.SupportsHash(h) -+} -+ -+func FIPSApprovedHash(h hash.Hash) bool { -+ return openssl.FIPSApprovedHash(h) -+} -+ -+func SupportsSHAKE(securityBits int) bool { -+ return openssl.SupportsSHAKE(securityBits) -+} -+ -+func SupportsCSHAKE(securityBits int) bool { -+ return openssl.SupportsCSHAKE(securityBits) -+} -+ -+func SupportsCurve(curve string) bool { -+ return openssl.SupportsCurve(curve) -+} -+ -+func SupportsRSAOAEPLabel(label []byte) bool { return true } -+ -+func SupportsRSAPKCS1v15Encryption() bool { -+ return openssl.SupportsRSAPKCS1v15Encryption() -+} -+ -+func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { -+ return openssl.SupportsRSAPKCS1v15Signature(hash) -+} -+ -+type Hash = openssl.Hash -+type SHAKE = openssl.SHAKE -+ -+func NewMD5() hash.Hash { return openssl.NewMD5() } -+func NewSHA1() hash.Hash { return openssl.NewSHA1() } -+func NewSHA224() hash.Hash { return openssl.NewSHA224() } -+func NewSHA256() hash.Hash { return openssl.NewSHA256() } -+func NewSHA384() hash.Hash { return openssl.NewSHA384() } -+func NewSHA512() hash.Hash { return openssl.NewSHA512() } -+func NewSHA512_224() hash.Hash { return openssl.NewSHA512_224() } -+func NewSHA512_256() hash.Hash { return openssl.NewSHA512_256() } -+func NewSHA3_224() *Hash { return openssl.NewSHA3_224() } -+func NewSHA3_256() *Hash { return openssl.NewSHA3_256() } -+func NewSHA3_384() *Hash { return openssl.NewSHA3_384() } -+func NewSHA3_512() *Hash { return openssl.NewSHA3_512() } -+ -+func NewSHAKE128() *SHAKE { return openssl.NewSHAKE128() } -+func NewSHAKE256() *SHAKE { return openssl.NewSHAKE256() } -+func NewCSHAKE128(N, S []byte) *SHAKE { return openssl.NewCSHAKE128(N, S) } -+func NewCSHAKE256(N, S []byte) *SHAKE { return openssl.NewCSHAKE256(N, S) } -+ -+func MD5(p []byte) (sum [16]byte) { return openssl.MD5(p) } -+func SHA1(p []byte) (sum [20]byte) { return openssl.SHA1(p) } -+func SHA224(p []byte) (sum [28]byte) { return openssl.SHA224(p) } -+func SHA256(p []byte) (sum [32]byte) { return openssl.SHA256(p) } -+func SHA384(p []byte) (sum [48]byte) { return openssl.SHA384(p) } -+func SHA512(p []byte) (sum [64]byte) { return openssl.SHA512(p) } -+func SHA512_224(p []byte) (sum [28]byte) { return openssl.SHA512_224(p) } -+func SHA512_256(p []byte) (sum [32]byte) { return openssl.SHA512_256(p) } -+func SumSHA3_224(p []byte) (sum [28]byte) { return openssl.SumSHA3_224(p) } -+func SumSHA3_256(p []byte) (sum [32]byte) { return openssl.SumSHA3_256(p) } -+func SumSHA3_384(p []byte) (sum [48]byte) { return openssl.SumSHA3_384(p) } -+func SumSHA3_512(p []byte) (sum [64]byte) { return openssl.SumSHA3_512(p) } -+ -+func SumSHAKE128(data []byte, length int) (sum []byte) { return openssl.SumSHAKE128(data, length) } -+func SumSHAKE256(data []byte, length int) (sum []byte) { return openssl.SumSHAKE256(data, length) } -+ -+func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { return openssl.NewHMAC(h, key) } -+ -+func NewAESCipher(key []byte) (cipher.Block, error) { return openssl.NewAESCipher(key) } -+func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { return openssl.NewGCMTLS(c) } -+func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { return openssl.NewGCMTLS13(c) } -+ -+type PublicKeyECDSA = openssl.PublicKeyECDSA -+type PrivateKeyECDSA = openssl.PrivateKeyECDSA -+ -+func GenerateKeyECDSA(curve string) (X, Y, D openssl.BigInt, err error) { -+ return openssl.GenerateKeyECDSA(curve) -+} -+ -+func NewPrivateKeyECDSA(curve string, X, Y, D openssl.BigInt) (*openssl.PrivateKeyECDSA, error) { -+ return openssl.NewPrivateKeyECDSA(curve, X, Y, D) -+} -+ -+func NewPublicKeyECDSA(curve string, X, Y openssl.BigInt) (*openssl.PublicKeyECDSA, error) { -+ return openssl.NewPublicKeyECDSA(curve, X, Y) -+} -+ -+func SignMarshalECDSA(priv *openssl.PrivateKeyECDSA, hash []byte) ([]byte, error) { -+ return openssl.SignMarshalECDSA(priv, hash) -+} -+ -+func VerifyECDSA(pub *openssl.PublicKeyECDSA, hash []byte, sig []byte) bool { -+ return openssl.VerifyECDSA(pub, hash, sig) -+} -+ -+func SupportsRSAPrivateKey(bits, primes int) bool { -+ // The built-in OpenSSL 3 providers and OpenSSL 1 do support n-prime RSA keys, -+ // but SCOSSL only supports 2-prime RSA keys. -+ // Only 2-prime RSA keys are FIPS compliant, other n having compatibility -+ // and security issues. Even crypto/rsa deprecated rsa.GenerateMultiPrimeKey as of Go 1.21. -+ // Given the above reasons, we only support what SCOSSL supports. -+ return primes == 2 && SupportsRSAPublicKey(bits) -+} -+ -+func SupportsRSAPublicKey(bits int) bool { -+ min := 1024 -+ if fips140.Enabled() { -+ // The built-in OpenSSL 3 FIPS provider requires at least 2048 bits for FIPS compliance. -+ min = 2048 -+ } -+ return bits >= min && bits%8 == 0 && bits <= 16384 -+} -+ -+func SupportsRSASaltLength(sign bool, salt int) bool { -+ return true -+} -+ -+type PublicKeyRSA = openssl.PublicKeyRSA -+type PrivateKeyRSA = openssl.PrivateKeyRSA -+ -+func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *openssl.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { -+ return openssl.DecryptRSAOAEP(h, mgfHash, priv, ciphertext, label) -+} -+ -+func DecryptRSAPKCS1(priv *openssl.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ return openssl.DecryptRSAPKCS1(priv, ciphertext) -+} -+ -+func DecryptRSANoPadding(priv *openssl.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ return openssl.DecryptRSANoPadding(priv, ciphertext) -+} -+ -+func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *openssl.PublicKeyRSA, msg, label []byte) ([]byte, error) { -+ return openssl.EncryptRSAOAEP(h, mgfHash, pub, msg, label) -+} -+ -+func EncryptRSAPKCS1(pub *openssl.PublicKeyRSA, msg []byte) ([]byte, error) { -+ return openssl.EncryptRSAPKCS1(pub, msg) -+} -+ -+func EncryptRSANoPadding(pub *openssl.PublicKeyRSA, msg []byte) ([]byte, error) { -+ return openssl.EncryptRSANoPadding(pub, msg) -+} -+ -+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv openssl.BigInt, err error) { -+ return openssl.GenerateKeyRSA(bits) -+} -+ -+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv openssl.BigInt) (*openssl.PrivateKeyRSA, error) { -+ return openssl.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) -+} -+ -+func NewPublicKeyRSA(N, E openssl.BigInt) (*openssl.PublicKeyRSA, error) { -+ return openssl.NewPublicKeyRSA(N, E) -+} -+ -+func SignRSAPKCS1v15(priv *openssl.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { -+ return openssl.SignRSAPKCS1v15(priv, h, hashed) -+} -+ -+func SignRSAPSS(priv *openssl.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { -+ return openssl.SignRSAPSS(priv, h, hashed, saltLen) -+} -+ -+func VerifyRSAPKCS1v15(pub *openssl.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { -+ return openssl.VerifyRSAPKCS1v15(pub, h, hashed, sig) -+} -+ -+func VerifyRSAPSS(pub *openssl.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { -+ return openssl.VerifyRSAPSS(pub, h, hashed, sig, saltLen) -+} -+ -+type PublicKeyECDH = openssl.PublicKeyECDH -+type PrivateKeyECDH = openssl.PrivateKeyECDH -+ -+func ECDH(priv *openssl.PrivateKeyECDH, pub *openssl.PublicKeyECDH) ([]byte, error) { -+ return openssl.ECDH(priv, pub) -+} -+ -+func GenerateKeyECDH(curve string) (*openssl.PrivateKeyECDH, []byte, error) { -+ return openssl.GenerateKeyECDH(curve) -+} -+ -+func NewPrivateKeyECDH(curve string, bytes []byte) (*openssl.PrivateKeyECDH, error) { -+ return openssl.NewPrivateKeyECDH(curve, bytes) -+} -+ -+func NewPublicKeyECDH(curve string, bytes []byte) (*openssl.PublicKeyECDH, error) { -+ return openssl.NewPublicKeyECDH(curve, bytes) -+} -+ -+func SupportsTLS13KDF() bool { -+ return openssl.SupportsTLS13KDF() -+} -+ -+func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { -+ return openssl.ExpandTLS13KDF(h, pseudorandomKey, label, context, keyLength) -+} -+ -+func SupportsHKDF() bool { -+ return openssl.SupportsHKDF() -+} -+ -+func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { -+ return openssl.ExpandHKDF(h, pseudorandomKey, info, keyLength) -+} -+ -+func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { -+ return openssl.ExtractHKDF(h, secret, salt) -+} -+ -+func SupportsPBKDF2() bool { -+ return openssl.SupportsPBKDF2() -+} -+ -+func PBKDF2(pass, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) { -+ return openssl.PBKDF2(pass, salt, iter, keyLen, h) -+} -+ -+func SupportsTLS1PRF() bool { -+ return openssl.SupportsTLS1PRF() -+} -+ -+func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { -+ return openssl.TLS1PRF(result, secret, label, seed, h) -+} -+ -+func SupportsDESCipher() bool { -+ return openssl.SupportsDESCipher() -+} -+ -+func SupportsTripleDESCipher() bool { -+ return openssl.SupportsTripleDESCipher() -+} -+ -+func NewDESCipher(key []byte) (cipher.Block, error) { -+ return openssl.NewDESCipher(key) -+} -+ -+func NewTripleDESCipher(key []byte) (cipher.Block, error) { -+ return openssl.NewTripleDESCipher(key) -+} -+ -+func SupportsRC4() bool { -+ return openssl.SupportsRC4() -+} -+ -+type RC4Cipher = openssl.RC4Cipher -+ -+func NewRC4Cipher(key []byte) (*RC4Cipher, error) { return openssl.NewRC4Cipher(key) } -+ -+func SupportsEd25519() bool { return openssl.SupportsEd25519() } -+ -+type PublicKeyEd25519 = *openssl.PublicKeyEd25519 -+type PrivateKeyEd25519 = *openssl.PrivateKeyEd25519 -+ -+func GenerateKeyEd25519() (PrivateKeyEd25519, error) { -+ return openssl.GenerateKeyEd25519() -+} -+ -+// Deprecated: use NewPrivateKeyEd25519 instead. -+func NewPrivateKeyEd25119(priv []byte) (PrivateKeyEd25519, error) { -+ return openssl.NewPrivateKeyEd25519(priv) -+} -+ -+// Deprecated: use NewPublicKeyEd25519 instead. -+func NewPublicKeyEd25119(pub []byte) (PublicKeyEd25519, error) { -+ return openssl.NewPublicKeyEd25519(pub) -+} -+ -+func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { -+ return openssl.NewPrivateKeyEd25519(priv) -+} -+ -+func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { -+ return openssl.NewPublicKeyEd25519(pub) -+} -+ -+func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { -+ return openssl.NewPrivateKeyEd25519FromSeed(seed) -+} -+ -+func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { -+ return openssl.SignEd25519(priv, message) -+} -+ -+func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { -+ return openssl.VerifyEd25519(pub, message, sig) -+} -+ -+type PublicKeyDSA = openssl.PublicKeyDSA -+type PrivateKeyDSA = openssl.PrivateKeyDSA -+ -+func SupportsDSA(l, n int) bool { -+ return openssl.SupportsDSA() -+} -+ -+func GenerateParametersDSA(l, n int) (p, q, g openssl.BigInt, err error) { -+ params, err := openssl.GenerateParametersDSA(l, n) -+ return params.P, params.Q, params.G, err -+} -+ -+func GenerateKeyDSA(p, q, g openssl.BigInt) (x, y openssl.BigInt, err error) { -+ return openssl.GenerateKeyDSA(openssl.DSAParameters{P: p, Q: q, G: g}) -+} -+ -+func NewPrivateKeyDSA(p, q, g, x, y openssl.BigInt) (*openssl.PrivateKeyDSA, error) { -+ return openssl.NewPrivateKeyDSA(openssl.DSAParameters{P: p, Q: q, G: g}, x, y) -+} -+ -+func NewPublicKeyDSA(p, q, g, y openssl.BigInt) (*openssl.PublicKeyDSA, error) { -+ return openssl.NewPublicKeyDSA(openssl.DSAParameters{P: p, Q: q, G: g}, y) -+} -+ -+func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (openssl.BigInt, openssl.BigInt, error)) (r, s openssl.BigInt, err error) { -+ sig, err := openssl.SignDSA(priv, hash) -+ if err != nil { -+ return nil, nil, err -+ } -+ -+ r, s, err = parseSignature(sig) -+ if err != nil { -+ return nil, nil, err -+ } -+ -+ return openssl.BigInt(r), openssl.BigInt(s), nil -+} -+ -+func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s openssl.BigInt, encodeSignature func(r, s openssl.BigInt) ([]byte, error)) bool { -+ sig, err := encodeSignature(r, s) -+ if err != nil { -+ return false -+ } -+ -+ return openssl.VerifyDSA(pub, hashed, sig) -+} -+ -+func SupportsMLKEM768() bool { -+ return openssl.SupportsMLKEM768() -+} -+ -+func SupportsMLKEM1024() bool { -+ return openssl.SupportsMLKEM1024() -+} -+ -+type DecapsulationKeyMLKEM768 = openssl.DecapsulationKeyMLKEM768 -+type EncapsulationKeyMLKEM768 = openssl.EncapsulationKeyMLKEM768 -+ -+func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { -+ return openssl.GenerateKeyMLKEM768() -+} -+ -+func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { -+ return openssl.NewDecapsulationKeyMLKEM768(seed) -+} -+ -+func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { -+ return openssl.NewEncapsulationKeyMLKEM768(encapsulationKey) -+} -+ -+type DecapsulationKeyMLKEM1024 = openssl.DecapsulationKeyMLKEM1024 -+type EncapsulationKeyMLKEM1024 = openssl.EncapsulationKeyMLKEM1024 -+ -+func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { -+ return openssl.GenerateKeyMLKEM1024() -+} -+ -+func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { -+ return openssl.NewDecapsulationKeyMLKEM1024(seed) -+} -+ -+func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { -+ return openssl.NewEncapsulationKeyMLKEM1024(encapsulationKey) -+} -+ -+func SupportsChaCha20Poly1305() bool { -+ return openssl.SupportsChaCha20Poly1305() -+} -+ -+func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { -+ if fips140only.Enforced() { -+ return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode") -+ } -+ return openssl.NewChaCha20Poly1305(key) -+} -diff --git a/src/crypto/internal/backend/backend_test.go b/src/crypto/internal/backend/backend_test.go -new file mode 100644 -index 00000000000000..093acd82556330 ---- /dev/null -+++ b/src/crypto/internal/backend/backend_test.go -@@ -0,0 +1,56 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+package backend -+ -+import ( -+ "testing" -+) -+ -+// Test that Unreachable panics. -+func TestUnreachable(t *testing.T) { -+ defer func() { -+ if Enabled { -+ if err := recover(); err == nil { -+ t.Fatal("expected Unreachable to panic") -+ } -+ } else { -+ if err := recover(); err != nil { -+ t.Fatalf("expected Unreachable to be a no-op") -+ } -+ } -+ }() -+ Unreachable() -+} -+ -+// Test that UnreachableExceptTests does not panic (this is a test). -+func TestUnreachableExceptTests(t *testing.T) { -+ UnreachableExceptTests() -+} -+ -+func TestSupportsRSAPrivateKey(t *testing.T) { -+ if !Enabled { -+ t.Skip("BoringCrypto not enabled") -+ } -+ tests := []struct { -+ bitLen int -+ numPrimes int -+ supported bool -+ }{ -+ {2048, 2, true}, -+ {3072, 2, true}, -+ {4096, 2, true}, -+ {2048, 3, false}, -+ {3072, 3, false}, -+ {4096, 3, false}, -+ } -+ for _, test := range tests { -+ t.Run("", func(t *testing.T) { -+ supported := SupportsRSAPrivateKey(test.bitLen, test.numPrimes) -+ if supported != test.supported { -+ t.Errorf("SupportsRSAPrivateKey(%d, %d) = %v; want %v", test.bitLen, test.numPrimes, supported, test.supported) -+ } -+ }) -+ } -+} -diff --git a/src/crypto/internal/backend/backend_windows.go b/src/crypto/internal/backend/backend_windows.go -new file mode 100644 -index 00000000000000..d25ef26f77013d ---- /dev/null -+++ b/src/crypto/internal/backend/backend_windows.go -@@ -0,0 +1,442 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+// Package cng provides access to CNGCrypto implementation functions. -+// Check the variable Enabled to find out whether CNGCrypto is available. -+// If CNGCrypto is not available, the functions in this package all panic. -+package backend -+ -+import ( -+ "crypto" -+ "crypto/cipher" -+ "crypto/internal/backend/fips140" -+ "crypto/internal/boring/sig" -+ "crypto/internal/fips140only" -+ "errors" -+ "hash" -+ _ "unsafe" -+ -+ "github.com/microsoft/go-crypto-winnative/cng" -+) -+ -+func init() { -+ // Windows is considered FIPS compliant. -+ if err := fips140.Check(func() bool { return true }); err != nil { -+ panic("cngcrypto: " + err.Error()) -+ } -+ sig.BoringCrypto() -+ fips140only.BackendApprovedHash = FIPSApprovedHash -+} -+ -+// Enabled controls whether FIPS crypto is enabled. -+const Enabled = true -+ -+type BigInt = cng.BigInt -+ -+const RandReader = cng.RandReader -+ -+func SupportsHash(h crypto.Hash) bool { -+ return cng.SupportsHash(h) -+} -+ -+func FIPSApprovedHash(h hash.Hash) bool { -+ return cng.FIPSApprovedHash(h) -+} -+ -+func SupportsSHAKE(securityBits int) bool { -+ return cng.SupportsSHAKE(securityBits) -+} -+ -+func SupportsCSHAKE(securityBits int) bool { -+ return cng.SupportsSHAKE(securityBits) -+} -+ -+func SupportsCurve(curve string) bool { -+ switch curve { -+ case "P-224", "P-256", "P-384", "P-521", "X25519": -+ return true -+ } -+ return false -+} -+ -+func SupportsRSAOAEPLabel(label []byte) bool { return true } -+func SupportsRSAPKCS1v15Encryption() bool { return true } -+ -+func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { -+ // 0 and MD5SHA1 are special cases that are always supported for PKCS1v15 signatures. -+ switch hash { -+ case 0, crypto.MD5SHA1: -+ return true -+ default: -+ return cng.SupportsHash(hash) -+ } -+} -+ -+type Hash = cng.Hash -+type SHAKE = cng.SHAKE -+ -+func NewMD5() hash.Hash { return cng.NewMD5() } -+func NewSHA1() hash.Hash { return cng.NewSHA1() } -+func NewSHA224() hash.Hash { panic("cngcrypto: not available") } -+func NewSHA256() hash.Hash { return cng.NewSHA256() } -+func NewSHA384() hash.Hash { return cng.NewSHA384() } -+func NewSHA512() hash.Hash { return cng.NewSHA512() } -+func NewSHA512_224() hash.Hash { panic("cngcrypto: not available") } -+func NewSHA512_256() hash.Hash { panic("cngcrypto: not available") } -+func NewSHA3_224() *Hash { panic("cngcrypto: not available") } -+func NewSHA3_256() *Hash { return cng.NewSHA3_256() } -+func NewSHA3_384() *Hash { return cng.NewSHA3_384() } -+func NewSHA3_512() *Hash { return cng.NewSHA3_512() } -+ -+func NewSHAKE128() *SHAKE { return cng.NewSHAKE128() } -+func NewSHAKE256() *SHAKE { return cng.NewSHAKE256() } -+func NewCSHAKE128(N, S []byte) *SHAKE { return cng.NewCSHAKE128(N, S) } -+func NewCSHAKE256(N, S []byte) *SHAKE { return cng.NewCSHAKE256(N, S) } -+ -+func MD5(p []byte) (sum [16]byte) { return cng.MD5(p) } -+func SHA1(p []byte) (sum [20]byte) { return cng.SHA1(p) } -+func SHA224(p []byte) (sum [28]byte) { panic("cngcrypto: not available") } -+func SHA256(p []byte) (sum [32]byte) { return cng.SHA256(p) } -+func SHA384(p []byte) (sum [48]byte) { return cng.SHA384(p) } -+func SHA512(p []byte) (sum [64]byte) { return cng.SHA512(p) } -+func SHA512_224(p []byte) (sum [28]byte) { panic("cngcrypto: not available") } -+func SHA512_256(p []byte) (sum [32]byte) { panic("cngcrypto: not available") } -+func SumSHA3_224(p []byte) (sum [28]byte) { panic("cngcrypto: not available") } -+func SumSHA3_256(p []byte) (sum [32]byte) { return cng.SumSHA3_256(p) } -+func SumSHA3_384(p []byte) (sum [48]byte) { return cng.SumSHA3_384(p) } -+func SumSHA3_512(p []byte) (sum [64]byte) { return cng.SumSHA3_512(p) } -+ -+func SumSHAKE128(data []byte, length int) (sum []byte) { return cng.SumSHAKE128(data, length) } -+func SumSHAKE256(data []byte, length int) (sum []byte) { return cng.SumSHAKE256(data, length) } -+ -+func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { -+ return cng.NewHMAC(h, key) -+} -+ -+func NewAESCipher(key []byte) (cipher.Block, error) { -+ return cng.NewAESCipher(key) -+} -+ -+func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { -+ return cng.NewGCMTLS(c) -+} -+ -+func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { -+ return cng.NewGCMTLS13(c) -+} -+ -+type PublicKeyECDSA = cng.PublicKeyECDSA -+type PrivateKeyECDSA = cng.PrivateKeyECDSA -+ -+func GenerateKeyECDSA(curve string) (X, Y, D cng.BigInt, err error) { -+ return cng.GenerateKeyECDSA(curve) -+} -+ -+func NewPrivateKeyECDSA(curve string, X, Y, D cng.BigInt) (*cng.PrivateKeyECDSA, error) { -+ return cng.NewPrivateKeyECDSA(curve, X, Y, D) -+} -+ -+func NewPublicKeyECDSA(curve string, X, Y cng.BigInt) (*cng.PublicKeyECDSA, error) { -+ return cng.NewPublicKeyECDSA(curve, X, Y) -+} -+ -+//go:linkname encodeSignature crypto/ecdsa.encodeSignature -+func encodeSignature(r, s []byte) ([]byte, error) -+ -+//go:linkname parseSignature crypto/ecdsa.parseSignature -+func parseSignature(sig []byte) (r, s []byte, err error) -+ -+func SignMarshalECDSA(priv *cng.PrivateKeyECDSA, hash []byte) ([]byte, error) { -+ r, s, err := cng.SignECDSA(priv, hash) -+ if err != nil { -+ return nil, err -+ } -+ return encodeSignature(r, s) -+} -+ -+func VerifyECDSA(pub *cng.PublicKeyECDSA, hash []byte, sig []byte) bool { -+ rBytes, sBytes, err := parseSignature(sig) -+ if err != nil { -+ return false -+ } -+ return cng.VerifyECDSA(pub, hash, cng.BigInt(rBytes), cng.BigInt(sBytes)) -+} -+ -+func SignECDSA(priv *cng.PrivateKeyECDSA, hash []byte) (r, s cng.BigInt, err error) { -+ return cng.SignECDSA(priv, hash) -+} -+ -+func VerifyECDSARaw(pub *cng.PublicKeyECDSA, hash []byte, r, s cng.BigInt) bool { -+ return cng.VerifyECDSA(pub, hash, r, s) -+} -+ -+func SupportsRSAPrivateKey(bits, primes int) bool { -+ return primes == 2 && SupportsRSAPublicKey(bits) -+} -+ -+func SupportsRSAPublicKey(bits int) bool { -+ return bits >= 512 && bits%8 == 0 && bits <= 16384 -+} -+ -+func SupportsRSASaltLength(sign bool, salt int) bool { -+ if sign { -+ return true -+ } -+ return salt != 0 // rsa.PSSSaltLengthAuto -+} -+ -+type PublicKeyRSA = cng.PublicKeyRSA -+type PrivateKeyRSA = cng.PrivateKeyRSA -+ -+func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *cng.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { -+ return cng.DecryptRSAOAEP(h, priv, ciphertext, label) -+} -+ -+func DecryptRSAPKCS1(priv *cng.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ return cng.DecryptRSAPKCS1(priv, ciphertext) -+} -+ -+func DecryptRSANoPadding(priv *cng.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ return cng.DecryptRSANoPadding(priv, ciphertext) -+} -+ -+func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *cng.PublicKeyRSA, msg, label []byte) ([]byte, error) { -+ return cng.EncryptRSAOAEP(h, pub, msg, label) -+} -+ -+func EncryptRSAPKCS1(pub *cng.PublicKeyRSA, msg []byte) ([]byte, error) { -+ return cng.EncryptRSAPKCS1(pub, msg) -+} -+ -+func EncryptRSANoPadding(pub *cng.PublicKeyRSA, msg []byte) ([]byte, error) { -+ return cng.EncryptRSANoPadding(pub, msg) -+} -+ -+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv cng.BigInt, err error) { -+ return cng.GenerateKeyRSA(bits) -+} -+ -+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv cng.BigInt) (*cng.PrivateKeyRSA, error) { -+ return cng.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) -+} -+ -+func NewPublicKeyRSA(N, E cng.BigInt) (*cng.PublicKeyRSA, error) { -+ return cng.NewPublicKeyRSA(N, E) -+} -+ -+func SignRSAPKCS1v15(priv *cng.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { -+ return cng.SignRSAPKCS1v15(priv, h, hashed) -+} -+ -+func SignRSAPSS(priv *cng.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { -+ return cng.SignRSAPSS(priv, h, hashed, saltLen) -+} -+ -+func VerifyRSAPKCS1v15(pub *cng.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { -+ return cng.VerifyRSAPKCS1v15(pub, h, hashed, sig) -+} -+ -+func VerifyRSAPSS(pub *cng.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { -+ return cng.VerifyRSAPSS(pub, h, hashed, sig, saltLen) -+} -+ -+type PrivateKeyECDH = cng.PrivateKeyECDH -+type PublicKeyECDH = cng.PublicKeyECDH -+ -+func ECDH(priv *cng.PrivateKeyECDH, pub *cng.PublicKeyECDH) ([]byte, error) { -+ return cng.ECDH(priv, pub) -+} -+ -+func GenerateKeyECDH(curve string) (*cng.PrivateKeyECDH, []byte, error) { -+ return cng.GenerateKeyECDH(curve) -+} -+ -+func NewPrivateKeyECDH(curve string, bytes []byte) (*cng.PrivateKeyECDH, error) { -+ return cng.NewPrivateKeyECDH(curve, bytes) -+} -+ -+func NewPublicKeyECDH(curve string, bytes []byte) (*cng.PublicKeyECDH, error) { -+ return cng.NewPublicKeyECDH(curve, bytes) -+} -+ -+func SupportsTLS13KDF() bool { -+ return false -+} -+ -+func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsHKDF() bool { -+ return cng.SupportsHKDF() -+} -+ -+func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { -+ return cng.ExpandHKDF(h, pseudorandomKey, info, keyLength) -+} -+ -+func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { -+ return cng.ExtractHKDF(h, secret, salt) -+} -+ -+func SupportsPBKDF2() bool { return true } -+ -+func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) { -+ return cng.PBKDF2(password, salt, iter, keyLen, h) -+} -+ -+func SupportsTLS1PRF() bool { -+ return true -+} -+ -+func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { -+ return cng.TLS1PRF(result, secret, label, seed, h) -+} -+ -+func SupportsDESCipher() bool { -+ return true -+} -+ -+func SupportsTripleDESCipher() bool { -+ return true -+} -+ -+func NewDESCipher(key []byte) (cipher.Block, error) { -+ return cng.NewDESCipher(key) -+} -+ -+func NewTripleDESCipher(key []byte) (cipher.Block, error) { -+ return cng.NewTripleDESCipher(key) -+} -+ -+func SupportsRC4() bool { return true } -+ -+type RC4Cipher = cng.RC4Cipher -+ -+func NewRC4Cipher(key []byte) (*RC4Cipher, error) { return cng.NewRC4Cipher(key) } -+ -+func SupportsEd25519() bool { return false } -+ -+type PublicKeyEd25519 struct{} -+ -+func (k PublicKeyEd25519) Bytes() ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+type PrivateKeyEd25519 struct{} -+ -+func (k PrivateKeyEd25519) Bytes() ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func GenerateKeyEd25519() (PrivateKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { -+ panic("cryptobackend: not available") -+} -+ -+type PrivateKeyDSA = cng.PrivateKeyDSA -+type PublicKeyDSA = cng.PublicKeyDSA -+ -+func SupportsDSA(l, n int) bool { -+ // These are the only N values supported by CNG -+ return n == 160 || n == 256 -+} -+ -+func GenerateParametersDSA(l, n int) (p, q, g cng.BigInt, err error) { -+ params, err := cng.GenerateParametersDSA(l) -+ if err != nil { -+ return nil, nil, nil, err -+ } -+ return params.P, params.Q, params.G, nil -+} -+ -+func GenerateKeyDSA(p, q, g cng.BigInt) (x, y cng.BigInt, err error) { -+ return cng.GenerateKeyDSA(cng.DSAParameters{P: p, Q: q, G: g}) -+} -+ -+func NewPrivateKeyDSA(p, q, g, x, y cng.BigInt) (*cng.PrivateKeyDSA, error) { -+ return cng.NewPrivateKeyDSA(cng.DSAParameters{P: p, Q: q, G: g}, x, y) -+} -+ -+func NewPublicKeyDSA(p, q, g, y cng.BigInt) (*cng.PublicKeyDSA, error) { -+ return cng.NewPublicKeyDSA(cng.DSAParameters{P: p, Q: q, G: g}, y) -+} -+ -+func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (cng.BigInt, cng.BigInt, error)) (r, s cng.BigInt, err error) { -+ return cng.SignDSA(priv, hash) -+} -+ -+func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s cng.BigInt, encodeSignature func(r, s cng.BigInt) ([]byte, error)) bool { -+ return cng.VerifyDSA(pub, hashed, r, s) -+} -+ -+func SupportsMLKEM768() bool { -+ return cng.SupportsMLKEM() -+} -+ -+func SupportsMLKEM1024() bool { -+ return cng.SupportsMLKEM() -+} -+ -+type DecapsulationKeyMLKEM768 = cng.DecapsulationKeyMLKEM768 -+type EncapsulationKeyMLKEM768 = cng.EncapsulationKeyMLKEM768 -+ -+func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { -+ return cng.GenerateKeyMLKEM768() -+} -+ -+func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { -+ return cng.NewDecapsulationKeyMLKEM768(seed) -+} -+ -+func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { -+ return cng.NewEncapsulationKeyMLKEM768(encapsulationKey) -+} -+ -+type DecapsulationKeyMLKEM1024 = cng.DecapsulationKeyMLKEM1024 -+type EncapsulationKeyMLKEM1024 = cng.EncapsulationKeyMLKEM1024 -+ -+func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { -+ return cng.GenerateKeyMLKEM1024() -+} -+ -+func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { -+ return cng.NewDecapsulationKeyMLKEM1024(seed) -+} -+ -+func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { -+ return cng.NewEncapsulationKeyMLKEM1024(encapsulationKey) -+} -+ -+func SupportsChaCha20Poly1305() bool { -+ return cng.SupportsChaCha20Poly1305() -+} -+ -+func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { -+ if fips140only.Enforced() { -+ return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode") -+ } -+ return cng.NewChaCha20Poly1305(key) -+} -diff --git a/src/crypto/internal/backend/bbig/big.go b/src/crypto/internal/backend/bbig/big.go -new file mode 100644 -index 00000000000000..20251a290dc2e0 ---- /dev/null -+++ b/src/crypto/internal/backend/bbig/big.go -@@ -0,0 +1,17 @@ -+// Copyright 2022 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !goexperiment.systemcrypto -+ -+package bbig -+ -+import "math/big" -+ -+func Enc(b *big.Int) []uint { -+ return nil -+} -+ -+func Dec(b []uint) *big.Int { -+ return nil -+} -diff --git a/src/crypto/internal/backend/bbig/big_darwin.go b/src/crypto/internal/backend/bbig/big_darwin.go -new file mode 100644 -index 00000000000000..889f2ff7c703d8 ---- /dev/null -+++ b/src/crypto/internal/backend/bbig/big_darwin.go -@@ -0,0 +1,12 @@ -+// Copyright 2022 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package bbig -+ -+import "github.com/microsoft/go-crypto-darwin/bbig" -+ -+var Enc = bbig.Enc -+var Dec = bbig.Dec -diff --git a/src/crypto/internal/backend/bbig/big_linux.go b/src/crypto/internal/backend/bbig/big_linux.go -new file mode 100644 -index 00000000000000..1b515fe6244a52 ---- /dev/null -+++ b/src/crypto/internal/backend/bbig/big_linux.go -@@ -0,0 +1,12 @@ -+// Copyright 2022 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package bbig -+ -+import "github.com/microsoft/go-crypto-openssl/bbig" -+ -+var Enc = bbig.Enc -+var Dec = bbig.Dec -diff --git a/src/crypto/internal/backend/bbig/big_windows.go b/src/crypto/internal/backend/bbig/big_windows.go -new file mode 100644 -index 00000000000000..f2c21a88bff471 ---- /dev/null -+++ b/src/crypto/internal/backend/bbig/big_windows.go -@@ -0,0 +1,12 @@ -+// Copyright 2022 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package bbig -+ -+import "github.com/microsoft/go-crypto-winnative/cng/bbig" -+ -+var Enc = bbig.Enc -+var Dec = bbig.Dec -diff --git a/src/crypto/internal/backend/common.go b/src/crypto/internal/backend/common.go -new file mode 100644 -index 00000000000000..a9682181ffa154 ---- /dev/null -+++ b/src/crypto/internal/backend/common.go -@@ -0,0 +1,46 @@ -+// Copyright 2022 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+package backend -+ -+import ( -+ "crypto/internal/boring/sig" -+ "runtime" -+) -+ -+// Unreachable marks code that should be unreachable -+// when backend is in use. -+func Unreachable() { -+ if Enabled { -+ panic("cryptobackend: invalid code execution") -+ } else { -+ // Code that's unreachable is exactly the code -+ // we want to detect for reporting standard Go crypto. -+ sig.StandardCrypto() -+ } -+} -+ -+// Provided by runtime.crypto_backend_runtime_arg0 to avoid os import. -+func runtime_arg0() string -+ -+func hasSuffix(s, t string) bool { -+ return len(s) > len(t) && s[len(s)-len(t):] == t -+} -+ -+// UnreachableExceptTests marks code that should be unreachable -+// when backend is in use. It panics. -+func UnreachableExceptTests() { -+ // runtime_arg0 is not supported on windows. -+ // We are going through the same code patch on linux, -+ // so if we are unintentionally calling an 'unreachable' function, -+ // we will catch it there. -+ if Enabled && runtime.GOOS != "windows" { -+ name := runtime_arg0() -+ // If ran on Windows we'd need to allow _test.exe and .test.exe as well. -+ if !hasSuffix(name, "_test") && !hasSuffix(name, ".test") { -+ println("cryptobackend: unexpected code execution in", name) -+ panic("cryptobackend: invalid code execution") -+ } -+ } -+} -diff --git a/src/crypto/internal/backend/fips140/fips140.go b/src/crypto/internal/backend/fips140/fips140.go -new file mode 100644 -index 00000000000000..6c3568d77d2c0a ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/fips140.go -@@ -0,0 +1,95 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+package fips140 -+ -+import ( -+ "errors" -+ "internal/godebug" -+ "runtime" -+ "syscall" -+) -+ -+var fips140GODEBUG = godebug.New("fips140") -+ -+// Enabled reports whether FIPS 140 mode is enabled by using GODEBUG, GOFIPS, GOLANG_FIPS, -+// the 'requirefips' build tag, or any other platform-specific mechanism. -+func Enabled() bool { -+ return enabled -+} -+ -+var enabled bool -+ -+// message is a human-readable message about how [Enabled] was set. -+var message string -+ -+func init() { -+ // TODO: Decide which environment variable to use. -+ // See https://github.com/microsoft/go/issues/397. -+ enabled, message = detect(fips140GODEBUG.Value(), syscall.Getenv, systemFIPSMode) -+ if isRequireFIPS { -+ if isSkipFIPSCheck { -+ panic("the 'requirefips' build tag is enabled, but it conflicts " + -+ "with the 'ms_skipfipscheck' build tag") -+ } -+ message = "requirefips tag set" -+ enabled = true -+ } -+} -+ -+// detect reports whether FIPS 140 mode should be enabled and returns a -+// human-readable message describing how the decision was made. -+// -+// godebug is the value of the fips140 GODEBUG setting. getenv is used to look -+// up the GOFIPS and GOLANG_FIPS environment variables and mirrors the -+// semantics of [syscall.Getenv]. systemFIPS reports whether the platform -+// indicates that FIPS mode should be enabled (e.g. the Linux kernel FIPS flag). -+// -+// The inputs are taken as parameters, rather than read directly, to make the -+// detection logic easy to test without depending on process state. -+func detect(godebug string, getenv func(string) (string, bool), systemFIPS func() bool) (enabled bool, message string) { -+ switch godebug { -+ case "on", "only", "debug": -+ return true, "environment variable GODEBUG=fips140=" + godebug -+ case "off": -+ // GODEBUG=fips140=off explicitly disables FIPS mode and bypasses -+ // the platform-specific FIPS detection (e.g. the Linux kernel FIPS flag). -+ // This is the only supported way to skip the platform FIPS detection. -+ return false, "environment variable GODEBUG=fips140=off" -+ } -+ // Only "1" is a meaningful value for GOFIPS and GOLANG_FIPS. Any other -+ // value (including "0" and the empty string) is treated as if the -+ // variable were unset, to match the documented behavior and to avoid -+ // silently bypassing the platform FIPS detection due to a typo or -+ // accidental setting. To explicitly disable FIPS mode and skip the -+ // platform FIPS detection, use GODEBUG=fips140=off. -+ if v, ok := getenv("GOFIPS"); ok && v == "1" { -+ return true, "environment variable GOFIPS=1" -+ } -+ if v, ok := getenv("GOLANG_FIPS"); ok && v == "1" { -+ return true, "environment variable GOLANG_FIPS=1" -+ } -+ if systemFIPS() { -+ return true, "system FIPS mode" -+ } -+ return false, "" -+} -+ -+// Check checks whether fips mode, as returned by fips(), -+// is valid given the current settings. -+func Check(fips func() bool) error { -+ if isSkipFIPSCheck || !enabled { -+ return nil -+ } -+ if !backendEnabled { -+ if runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin" { -+ return errors.New("FIPS mode requested (" + message + ") but no crypto backend is supported on " + runtime.GOOS) -+ } -+ return errors.New("FIPS mode requested (" + message + ") but no supported crypto backend is enabled") -+ } -+ if !fips() { -+ return errors.New("FIPS mode requested (" + message + ") but not available") -+ } -+ return nil -+} -diff --git a/src/crypto/internal/backend/fips140/fips140_test.go b/src/crypto/internal/backend/fips140/fips140_test.go -new file mode 100644 -index 00000000000000..9fb7df809baed8 ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/fips140_test.go -@@ -0,0 +1,75 @@ -+// Copyright 2026 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+package fips140 -+ -+import ( -+ "testing" -+) -+ -+// fakeEnv returns a getenv function that serves lookups from m, with the -+// same (value, ok) semantics as [syscall.Getenv]. A key present in m with an -+// empty string value represents a set-but-empty environment variable. -+func fakeEnv(m map[string]string) func(string) (string, bool) { -+ return func(key string) (string, bool) { -+ v, ok := m[key] -+ return v, ok -+ } -+} -+ -+// TestDetect exercises the FIPS 140 detection logic in isolation, without -+// depending on process environment variables or the host's kernel FIPS -+// configuration. This lets us cover combinations that would otherwise be -+// impossible to test on a single machine, such as the kernel FIPS flag -+// being set or not set for the same test run. -+func TestDetect(t *testing.T) { -+ tests := []struct { -+ name string -+ godebug string -+ env map[string]string -+ systemFIPS bool -+ wantEnabled bool -+ wantMessage string -+ }{ -+ // GODEBUG=fips140 takes precedence over everything else. -+ {name: "GODEBUG=on", godebug: "on", wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=on"}, -+ {name: "GODEBUG=only", godebug: "only", wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=only"}, -+ {name: "GODEBUG=debug", godebug: "debug", wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=debug"}, -+ {name: "GODEBUG=on beats GOFIPS=0 and kernel FIPS", godebug: "on", env: map[string]string{"GOFIPS": "0"}, systemFIPS: true, wantEnabled: true, wantMessage: "environment variable GODEBUG=fips140=on"}, -+ {name: "GODEBUG=off beats kernel FIPS", godebug: "off", systemFIPS: true, wantEnabled: false, wantMessage: "environment variable GODEBUG=fips140=off"}, -+ {name: "GODEBUG=off beats GOFIPS=1", godebug: "off", env: map[string]string{"GOFIPS": "1"}, wantEnabled: false, wantMessage: "environment variable GODEBUG=fips140=off"}, -+ -+ // GOFIPS=1 enables FIPS when GODEBUG is unset/empty/unrecognized. -+ {name: "GOFIPS=1", env: map[string]string{"GOFIPS": "1"}, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, -+ {name: "GOFIPS=1 beats kernel FIPS off", env: map[string]string{"GOFIPS": "1"}, systemFIPS: false, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, -+ {name: "GODEBUG unrecognized, GOFIPS=1", godebug: "bogus", env: map[string]string{"GOFIPS": "1"}, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, -+ -+ // Non-"1" values of GOFIPS are ignored and fall through. -+ {name: "GOFIPS=0 falls through to kernel FIPS on", env: map[string]string{"GOFIPS": "0"}, systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, -+ {name: "GOFIPS=0 falls through to kernel FIPS off", env: map[string]string{"GOFIPS": "0"}, systemFIPS: false, wantEnabled: false, wantMessage: ""}, -+ {name: "GOFIPS empty falls through to kernel FIPS on", env: map[string]string{"GOFIPS": ""}, systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, -+ {name: "GOFIPS=garbage falls through", env: map[string]string{"GOFIPS": "garbage"}, systemFIPS: false, wantEnabled: false, wantMessage: ""}, -+ -+ // GOLANG_FIPS behaves the same as GOFIPS. -+ {name: "GOLANG_FIPS=1", env: map[string]string{"GOLANG_FIPS": "1"}, wantEnabled: true, wantMessage: "environment variable GOLANG_FIPS=1"}, -+ {name: "GOFIPS=1 beats GOLANG_FIPS=0", env: map[string]string{"GOFIPS": "1", "GOLANG_FIPS": "0"}, wantEnabled: true, wantMessage: "environment variable GOFIPS=1"}, -+ {name: "GOLANG_FIPS=0 falls through to kernel FIPS on", env: map[string]string{"GOLANG_FIPS": "0"}, systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, -+ -+ // With nothing set, the kernel FIPS flag controls the result. -+ {name: "nothing set, kernel FIPS on", systemFIPS: true, wantEnabled: true, wantMessage: "system FIPS mode"}, -+ {name: "nothing set, kernel FIPS off", systemFIPS: false, wantEnabled: false, wantMessage: ""}, -+ } -+ -+ for _, tc := range tests { -+ t.Run(tc.name, func(t *testing.T) { -+ systemFIPS := func() bool { return tc.systemFIPS } -+ gotEnabled, gotMessage := detect(tc.godebug, fakeEnv(tc.env), systemFIPS) -+ if gotEnabled != tc.wantEnabled || gotMessage != tc.wantMessage { -+ t.Errorf("detect(godebug=%q, env=%v, systemFIPS=%v) = (%v, %q), want (%v, %q)", -+ tc.godebug, tc.env, tc.systemFIPS, -+ gotEnabled, gotMessage, tc.wantEnabled, tc.wantMessage) -+ } -+ }) -+ } -+} -diff --git a/src/crypto/internal/backend/fips140/isrequirefips.go b/src/crypto/internal/backend/fips140/isrequirefips.go -new file mode 100644 -index 00000000000000..b33d08c84e2dae ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/isrequirefips.go -@@ -0,0 +1,9 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build requirefips -+ -+package fips140 -+ -+const isRequireFIPS = true -\ No newline at end of file -diff --git a/src/crypto/internal/backend/fips140/norequirefips.go b/src/crypto/internal/backend/fips140/norequirefips.go -new file mode 100644 -index 00000000000000..6f01b9a3524dee ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/norequirefips.go -@@ -0,0 +1,9 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !requirefips -+ -+package fips140 -+ -+const isRequireFIPS = false -\ No newline at end of file -diff --git a/src/crypto/internal/backend/fips140/nosystemcrypto.go b/src/crypto/internal/backend/fips140/nosystemcrypto.go -new file mode 100644 -index 00000000000000..289d6594eac54b ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/nosystemcrypto.go -@@ -0,0 +1,13 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !goexperiment.systemcrypto -+ -+package fips140 -+ -+const backendEnabled = false -+ -+func systemFIPSMode() bool { -+ return false -+} -diff --git a/src/crypto/internal/backend/fips140/requirefips_nosystemcrypto.go b/src/crypto/internal/backend/fips140/requirefips_nosystemcrypto.go -new file mode 100644 -index 00000000000000..dad6bcea9451e2 ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/requirefips_nosystemcrypto.go -@@ -0,0 +1,15 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build requirefips && !goexperiment.systemcrypto -+ -+package fips140 -+ -+func init() { -+ ` -+ The requirefips tag is enabled, but no crypto backend is enabled. -+ A crypto backend is required to enable FIPS mode. -+ For more information, visit https://github.com/microsoft/go/tree/microsoft/main/eng/doc/fips -+ ` -+} -diff --git a/src/crypto/internal/backend/fips140/skipfipscheck_off.go b/src/crypto/internal/backend/fips140/skipfipscheck_off.go -new file mode 100644 -index 00000000000000..f60b10dfa3aaf3 ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/skipfipscheck_off.go -@@ -0,0 +1,9 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !ms_skipfipscheck -+ -+package fips140 -+ -+const isSkipFIPSCheck = false -diff --git a/src/crypto/internal/backend/fips140/skipfipscheck_on.go b/src/crypto/internal/backend/fips140/skipfipscheck_on.go -new file mode 100644 -index 00000000000000..d3d39fd77f98db ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/skipfipscheck_on.go -@@ -0,0 +1,9 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build ms_skipfipscheck -+ -+package fips140 -+ -+const isSkipFIPSCheck = true -diff --git a/src/crypto/internal/backend/fips140/systemfips_darwin.go b/src/crypto/internal/backend/fips140/systemfips_darwin.go -new file mode 100644 -index 00000000000000..606a2db4dd685f ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/systemfips_darwin.go -@@ -0,0 +1,13 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package fips140 -+ -+const backendEnabled = true -+ -+func systemFIPSMode() bool { -+ return false -+} -diff --git a/src/crypto/internal/backend/fips140/systemfips_linux.go b/src/crypto/internal/backend/fips140/systemfips_linux.go -new file mode 100644 -index 00000000000000..2c65c264dc84e1 ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/systemfips_linux.go -@@ -0,0 +1,59 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package fips140 -+ -+import ( -+ _ "crypto/internal/backend/internal/opensslsetup" -+ "syscall" -+ -+ "github.com/microsoft/go-crypto-openssl/osslsetup" -+) -+ -+const backendEnabled = true -+ -+// systemFIPSMode reports whether the system is in FIPS mode. -+// It first checks the kernel, and if that is not available, it checks the -+// OpenSSL library. -+func systemFIPSMode() bool { -+ if kernelFIPSMode() { -+ return true -+ } -+ return osslsetup.FIPS() -+} -+ -+// kernelFIPSMode reports whether the kernel is in FIPS mode. -+func kernelFIPSMode() bool { -+ var fd int -+ for { -+ var err error -+ fd, err = syscall.Open("/proc/sys/crypto/fips_enabled", syscall.O_RDONLY, 0) -+ if err == nil { -+ break -+ } -+ switch err { -+ case syscall.EINTR: -+ continue -+ case syscall.ENOENT: -+ return false -+ default: -+ // If there is an error reading we could either panic or assume FIPS is not enabled. -+ // Panicking would be too disruptive for apps that don't require FIPS. -+ // If an app wants to be 100% sure that is running in FIPS mode -+ // it should use fips140.Enabled() or GODEBUG=fips140=1. -+ return false -+ } -+ } -+ defer syscall.Close(fd) -+ var tmp [1]byte -+ n, err := syscall.Read(fd, tmp[:]) -+ if n != 1 || err != nil { -+ // We return false instead of panicing for the same reason as before. -+ return false -+ } -+ // fips_enabled can be either '0' or '1'. -+ return tmp[0] == '1' -+} -diff --git a/src/crypto/internal/backend/fips140/systemfips_windows.go b/src/crypto/internal/backend/fips140/systemfips_windows.go -new file mode 100644 -index 00000000000000..a705e7bc4fc536 ---- /dev/null -+++ b/src/crypto/internal/backend/fips140/systemfips_windows.go -@@ -0,0 +1,35 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package fips140 -+ -+import ( -+ "internal/syscall/windows/sysdll" -+ "syscall" -+ "unsafe" -+) -+ -+const backendEnabled = true -+ -+// Don't use github.com/microsoft/go-crypto-winnative here. -+// The fips140 package should have minimal dependencies. -+// Also, don't directly query the system FIPS mode from the registry, -+// there are some no-longer documented legacy entries that can enable FIPS mode, -+// and BCryptGetFipsAlgorithmMode supports them all. -+var ( -+ bcrypt = syscall.MustLoadDLL(sysdll.Add("bcrypt.dll")) -+ -+ bcryptGetFipsAlgorithmMode = bcrypt.MustFindProc("BCryptGetFipsAlgorithmMode") -+) -+ -+func systemFIPSMode() bool { -+ var enabled uint32 -+ ret, _, _ := bcryptGetFipsAlgorithmMode.Call(uintptr(unsafe.Pointer(&enabled))) -+ if ret != 0 { -+ return false -+ } -+ return enabled != 0 -+} -diff --git a/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go -new file mode 100644 -index 00000000000000..350c8ee7fa2bc6 ---- /dev/null -+++ b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux.go -@@ -0,0 +1,68 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+// opensslsetup is a package that initializes the OpenSSL library. -+// It doesn't export any symbol, but blank importing it has the -+// side effect of initializing the OpenSSL library. -+package opensslsetup -+ -+import ( -+ "syscall" -+ -+ "github.com/microsoft/go-crypto-openssl/osslsetup" -+) -+ -+// knownVersions is a list of supported and well-known libcrypto.so suffixes in decreasing version order. -+// FreeBSD library version numbering does not directly align to the version of osslsetup. -+// Its preferred search order is 11 -> 111. -+var knownVersions = [...]string{"3", "1.1", "11", "111"} -+ -+const lcryptoPrefix = "libcrypto.so." -+ -+func init() { -+ lib := library() -+ if err := osslsetup.Init(lib); err != nil { -+ panic("opensslcrypto: can't initialize OpenSSL " + lib + ": " + err.Error()) -+ } -+} -+ -+// library returns the name of the OpenSSL library to use. -+// It first checks the environment variable GO_OPENSSL_VERSION_OVERRIDE. -+// If that is not set, it searches a well-known list of library names. -+// If no library is found, it returns "libcrypto.so". -+func library() string { -+ if version, _ := syscall.Getenv("GO_OPENSSL_VERSION_OVERRIDE"); version != "" { -+ return lcryptoPrefix + version -+ } -+ if lib := searchKnownLibrary(); lib != "" { -+ return lib -+ } -+ return lcryptoPrefix[:len(lcryptoPrefix)-1] // no version found, try without version suffix -+} -+ -+// checkVersion is a variable that holds the osslsetup.CheckVersion function. -+// It is initialized in the init function to allow overriding in tests. -+var checkVersion = osslsetup.CheckVersion -+ -+// searchKnownLibrary returns the name of the highest available FIPS-enabled version of OpenSSL -+// using the known library suffixes. -+// If no FIPS-enabled version is found, it returns the name of the highest available version. -+// If no version is found, it returns an empty string. -+func searchKnownLibrary() string { -+ var lcryptoFallback string -+ for _, v := range knownVersions { -+ lcryptoCandidate := lcryptoPrefix + v -+ if exists, fips := checkVersion(lcryptoCandidate); exists { -+ if fips { -+ return lcryptoCandidate -+ } -+ if lcryptoFallback == "" { -+ lcryptoFallback = lcryptoCandidate -+ } -+ } -+ } -+ return lcryptoFallback -+} -diff --git a/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go -new file mode 100644 -index 00000000000000..2e45358029f484 ---- /dev/null -+++ b/src/crypto/internal/backend/internal/opensslsetup/opensslsetup_linux_test.go -@@ -0,0 +1,92 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto && cgo -+ -+package opensslsetup -+ -+import "testing" -+ -+func mockCheckVersion(t *testing.T, fn func(string) (bool, bool)) { -+ original := checkVersion -+ t.Cleanup(func() { -+ checkVersion = original -+ }) -+ checkVersion = fn -+} -+ -+func assertLibrary(t *testing.T, expected string) { -+ if result := library(); result != expected { -+ t.Errorf("expected %s, got %s", expected, result) -+ } -+} -+ -+func TestLibraryWithEnvOverride(t *testing.T) { -+ t.Setenv("GO_OPENSSL_VERSION_OVERRIDE", "1.1") -+ mockCheckVersion(t, func(s string) (bool, bool) { return false, false }) -+ assertLibrary(t, "libcrypto.so.1.1") -+} -+ -+func TestLibraryWithKnownVersion(t *testing.T) { -+ t.Setenv("GO_OPENSSL_VERSION_OVERRIDE", "") -+ -+ const maxLib = "libcrypto.so.3" -+ -+ t.Run("AllExistsNoneFIPS", func(t *testing.T) { -+ mockCheckVersion(t, func(s string) (bool, bool) { -+ return true, false -+ }) -+ for _, v := range knownVersions { -+ t.Run(v, func(t *testing.T) { -+ assertLibrary(t, maxLib) -+ }) -+ } -+ }) -+ -+ t.Run("OnlyOneExists", func(t *testing.T) { -+ for _, v := range knownVersions { -+ t.Run(v, func(t *testing.T) { -+ expected := "libcrypto.so." + v -+ mockCheckVersion(t, func(s string) (bool, bool) { -+ if s == expected { -+ return true, false -+ } -+ return false, false -+ }) -+ assertLibrary(t, expected) -+ }) -+ } -+ }) -+ -+ t.Run("AllExistsOnlyOneFIPS", func(t *testing.T) { -+ fipsLib := "libcrypto.so.1.1" -+ mockCheckVersion(t, func(s string) (bool, bool) { -+ return true, s == fipsLib -+ }) -+ for _, v := range knownVersions { -+ t.Run(v, func(t *testing.T) { -+ assertLibrary(t, fipsLib) -+ }) -+ } -+ }) -+ -+ t.Run("AllExistsAndAreFIPS", func(t *testing.T) { -+ mockCheckVersion(t, func(s string) (bool, bool) { -+ return true, true -+ }) -+ for _, v := range knownVersions { -+ t.Run(v, func(t *testing.T) { -+ assertLibrary(t, maxLib) -+ }) -+ } -+ }) -+} -+ -+func TestLibraryNoVersionFound(t *testing.T) { -+ t.Setenv("GO_OPENSSL_VERSION_OVERRIDE", "") -+ mockCheckVersion(t, func(string) (bool, bool) { -+ return false, false -+ }) -+ assertLibrary(t, "libcrypto.so") -+} -diff --git a/src/crypto/internal/backend/internal/opensslsetup/stub.go b/src/crypto/internal/backend/internal/opensslsetup/stub.go -new file mode 100644 -index 00000000000000..19fd29e19e7b96 ---- /dev/null -+++ b/src/crypto/internal/backend/internal/opensslsetup/stub.go -@@ -0,0 +1,8 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+// Placeholder to allow the opensslsetup package to be imported -+// without cgo enabled or without goexperiment.systemcrypto on linux. -+ -+package opensslsetup -diff --git a/src/crypto/internal/backend/nobackend.go b/src/crypto/internal/backend/nobackend.go -new file mode 100644 -index 00000000000000..cdc768b1e4b57d ---- /dev/null -+++ b/src/crypto/internal/backend/nobackend.go -@@ -0,0 +1,376 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !goexperiment.systemcrypto -+ -+package backend -+ -+import ( -+ "crypto" -+ "crypto/cipher" -+ "crypto/internal/backend/fips140" -+ "hash" -+ "io" -+) -+ -+func init() { -+ if err := fips140.Check(func() bool { return false }); err != nil { -+ panic(err) -+ } -+} -+ -+const Enabled = false -+ -+type BigInt = []uint -+ -+type randReader int -+ -+func (randReader) Read(b []byte) (int, error) { panic("cryptobackend: not available") } -+ -+const RandReader = randReader(0) -+ -+func SupportsHash(h crypto.Hash) bool { panic("cryptobackend: not available") } -+func FIPSApprovedHash(h hash.Hash) bool { panic("cryptobackend: not available") } -+ -+func SupportsSHAKE(securityBits int) bool { panic("cryptobackend: not available") } -+func SupportsCSHAKE(securityBits int) bool { panic("cryptobackend: not available") } -+ -+func SupportsCurve(curve string) bool { panic("cryptobackend: not available") } -+func SupportsRSAOAEPLabel(label []byte) bool { panic("cryptobackend: not available") } -+func SupportsRSAPKCS1v15Encryption() bool { panic("cryptobackend: not available") } -+func SupportsRSAPKCS1v15Signature(hash crypto.Hash) bool { panic("cryptobackend: not available") } -+ -+type Hash struct { -+ hash.Cloner -+} -+ -+func (d *Hash) MarshalBinary() ([]byte, error) { panic("cryptobackend: not available") } -+func (d *Hash) AppendBinary(p []byte) ([]byte, error) { panic("cryptobackend: not available") } -+func (d *Hash) UnmarshalBinary(data []byte) error { panic("cryptobackend: not available") } -+ -+type SHAKE struct { -+ io.Reader -+ hash.Hash -+} -+ -+func (s *SHAKE) MarshalBinary() ([]byte, error) { panic("cryptobackend: not available") } -+func (s *SHAKE) AppendBinary(p []byte) ([]byte, error) { panic("cryptobackend: not available") } -+func (s *SHAKE) UnmarshalBinary(data []byte) error { panic("cryptobackend: not available") } -+ -+func NewMD5() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA1() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA224() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA256() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA384() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA512() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA512_224() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA512_256() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA3_224() *Hash { panic("cryptobackend: not available") } -+func NewSHA3_256() *Hash { panic("cryptobackend: not available") } -+func NewSHA3_384() *Hash { panic("cryptobackend: not available") } -+func NewSHA3_512() *Hash { panic("cryptobackend: not available") } -+ -+func NewSHAKE128() *SHAKE { panic("cryptobackend: not available") } -+func NewSHAKE256() *SHAKE { panic("cryptobackend: not available") } -+func NewCSHAKE128(N, S []byte) *SHAKE { panic("cryptobackend: not available") } -+func NewCSHAKE256(N, S []byte) *SHAKE { panic("cryptobackend: not available") } -+ -+func MD5(p []byte) (sum [16]byte) { panic("cryptobackend: not available") } -+func SHA1(p []byte) (sum [20]byte) { panic("cryptobackend: not available") } -+func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SHA256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } -+func SHA384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } -+func SHA512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } -+func SHA512_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SHA512_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } -+func SumSHA3_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SumSHA3_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } -+func SumSHA3_384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } -+func SumSHA3_512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } -+ -+func SumSHAKE128(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } -+func SumSHAKE256(data []byte, length int) (sum []byte) { panic("cryptobackend: not available") } -+ -+func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("cryptobackend: not available") } -+ -+func NewAESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } -+func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } -+func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } -+ -+type PublicKeyECDSA struct{ _ int } -+type PrivateKeyECDSA struct{ _ int } -+ -+func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+func NewPrivateKeyECDSA(curve string, X, Y, D BigInt) (*PrivateKeyECDSA, error) { -+ panic("cryptobackend: not available") -+} -+func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) { -+ panic("cryptobackend: not available") -+} -+func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool { -+ panic("cryptobackend: not available") -+} -+func SupportsRSAPrivateKey(bits, primes int) bool { panic("cryptobackend: not available") } -+func SupportsRSAPublicKey(bits int) bool { panic("cryptobackend: not available") } -+func SupportsRSASaltLength(sign bool, salt int) bool { panic("cryptobackend: not available") } -+ -+type PublicKeyRSA struct{ _ int } -+type PrivateKeyRSA struct{ _ int } -+ -+func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv BigInt) (*PrivateKeyRSA, error) { -+ panic("cryptobackend: not available") -+} -+func NewPublicKeyRSA(N, E BigInt) (*PublicKeyRSA, error) { -+ panic("cryptobackend: not available") -+} -+func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { -+ panic("cryptobackend: not available") -+} -+func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { -+ panic("cryptobackend: not available") -+} -+ -+type PublicKeyECDH struct{} -+type PrivateKeyECDH struct{} -+ -+func ECDH(*PrivateKeyECDH, *PublicKeyECDH) ([]byte, error) { panic("cryptobackend: not available") } -+func GenerateKeyECDH(string) (*PrivateKeyECDH, []byte, error) { panic("cryptobackend: not available") } -+func NewPrivateKeyECDH(string, []byte) (*PrivateKeyECDH, error) { -+ panic("cryptobackend: not available") -+} -+func NewPublicKeyECDH(string, []byte) (*PublicKeyECDH, error) { panic("cryptobackend: not available") } -+func (*PublicKeyECDH) Bytes() []byte { panic("cryptobackend: not available") } -+func (*PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) { panic("cryptobackend: not available") } -+ -+func SupportsTLS13KDF() bool { panic("cryptobackend: not available") } -+ -+func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsHKDF() bool { panic("cryptobackend: not available") } -+ -+func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte, keyLength int) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsPBKDF2() bool { panic("cryptobackend: not available") } -+ -+func PBKDF2(password, salt []byte, iter, keyLen int, fh func() hash.Hash) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsTLS1PRF() bool { panic("cryptobackend: not available") } -+ -+func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsDESCipher() bool { panic("cryptobackend: not available") } -+ -+func SupportsTripleDESCipher() bool { panic("cryptobackend: not available") } -+ -+func NewDESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } -+ -+func NewTripleDESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } -+ -+func SupportsRC4() bool { panic("cryptobackend: not available") } -+ -+type RC4Cipher struct{} -+ -+func (c *RC4Cipher) Reset() { panic("cryptobackend: not available") } -+func (c *RC4Cipher) XORKeyStream(dst, src []byte) { panic("cryptobackend: not available") } -+ -+func NewRC4Cipher(key []byte) (*RC4Cipher, error) { panic("cryptobackend: not available") } -+ -+func SupportsEd25519() bool { panic("cryptobackend: not available") } -+ -+type PublicKeyEd25519 struct{} -+ -+func (k PublicKeyEd25519) Bytes() ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+type PrivateKeyEd25519 struct{} -+ -+func (k PrivateKeyEd25519) Bytes() ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func GenerateKeyEd25519() (PrivateKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPrivateKeyEd25519(priv []byte) (PrivateKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPublicKeyEd25519(pub []byte) (PublicKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPrivateKeyEd25519FromSeed(seed []byte) (PrivateKeyEd25519, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SignEd25519(priv PrivateKeyEd25519, message []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -+ -+func VerifyEd25519(pub PublicKeyEd25519, message, sig []byte) error { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsDSA(l, n int) bool { -+ panic("cryptobackend: not available") -+} -+ -+func GenerateParametersDSA(l, n int) (p, q, g BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+ -+type PublicKeyDSA struct{ _ int } -+type PrivateKeyDSA struct{ _ int } -+ -+func GenerateKeyDSA(p, q, g BigInt) (x, y BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPrivateKeyDSA(p, q, g, x, y BigInt) (*PrivateKeyDSA, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewPublicKeyDSA(p, q, g, y BigInt) (*PublicKeyDSA, error) { -+ panic("cryptobackend: not available") -+} -+ -+func SignDSA(priv *PrivateKeyDSA, hash []byte, parseSignature func([]byte) (BigInt, BigInt, error)) (r, s BigInt, err error) { -+ panic("cryptobackend: not available") -+} -+ -+func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s BigInt, encodeSignature func(r, s BigInt) ([]byte, error)) bool { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsMLKEM768() bool { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsMLKEM1024() bool { -+ panic("cryptobackend: not available") -+} -+ -+type DecapsulationKeyMLKEM768 struct{} -+type EncapsulationKeyMLKEM768 struct{} -+ -+func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewEncapsulationKeyMLKEM768(encapsulationKey []byte) (EncapsulationKeyMLKEM768, error) { -+ panic("cryptobackend: not available") -+} -+ -+func (dk DecapsulationKeyMLKEM768) Bytes() []byte { -+ panic("cryptobackend: not available") -+} -+ -+func (dk DecapsulationKeyMLKEM768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { -+ panic("cryptobackend: not available") -+} -+ -+func (dk DecapsulationKeyMLKEM768) EncapsulationKey() EncapsulationKeyMLKEM768 { -+ panic("cryptobackend: not available") -+} -+ -+func (ek EncapsulationKeyMLKEM768) Bytes() []byte { -+ panic("cryptobackend: not available") -+} -+ -+func (ek EncapsulationKeyMLKEM768) Encapsulate() (sharedKey, ciphertext []byte) { -+ panic("cryptobackend: not available") -+} -+ -+type DecapsulationKeyMLKEM1024 struct{} -+type EncapsulationKeyMLKEM1024 struct{} -+ -+func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) { -+ panic("cryptobackend: not available") -+} -+ -+func NewEncapsulationKeyMLKEM1024(encapsulationKey []byte) (EncapsulationKeyMLKEM1024, error) { -+ panic("cryptobackend: not available") -+} -+ -+func (dk DecapsulationKeyMLKEM1024) Bytes() []byte { -+ panic("cryptobackend: not available") -+} -+ -+func (dk DecapsulationKeyMLKEM1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { -+ panic("cryptobackend: not available") -+} -+ -+func (dk DecapsulationKeyMLKEM1024) EncapsulationKey() EncapsulationKeyMLKEM1024 { -+ panic("cryptobackend: not available") -+} -+ -+func (ek EncapsulationKeyMLKEM1024) Bytes() []byte { -+ panic("cryptobackend: not available") -+} -+ -+func (ek EncapsulationKeyMLKEM1024) Encapsulate() (sharedKey, ciphertext []byte) { -+ panic("cryptobackend: not available") -+} -+ -+func SupportsChaCha20Poly1305() bool { -+ return false -+} -+ -+func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { -+ panic("cryptobackend: not available") -+} -diff --git a/src/crypto/internal/backend/stub.s b/src/crypto/internal/backend/stub.s -new file mode 100644 -index 00000000000000..5e4b436554d44d ---- /dev/null -+++ b/src/crypto/internal/backend/stub.s -@@ -0,0 +1,10 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+// runtime_arg0 is declared in common.go without a body. -+// It's provided by package runtime, -+// but the go command doesn't know that. -+// Having this assembly file keeps the go command -+// from complaining about the missing body -+// (because the implementation might be here). -diff --git a/src/crypto/internal/fips140only/fips140only.go b/src/crypto/internal/fips140only/fips140only.go -index a8d840b17022cc..2a17f7da2d4aaa 100644 ---- a/src/crypto/internal/fips140only/fips140only.go -+++ b/src/crypto/internal/fips140only/fips140only.go -@@ -18,11 +18,18 @@ func Enforced() bool { - return fips140.Enforced() - } - -+// BackendApprovedHash is set by a crypto backend during init to provide -+// backend-specific FIPS hash approval checking. If nil, only the standard -+// library FIPS hash types are recognized as approved. -+var BackendApprovedHash func(h hash.Hash) bool -+ - func ApprovedHash(h hash.Hash) bool { - switch h.(type) { - case *sha256.Digest, *sha512.Digest, *sha3.Digest: - return true -- default: -- return false - } -+ if BackendApprovedHash != nil { -+ return BackendApprovedHash(h) -+ } -+ return false - } -diff --git a/src/crypto/systemcrypto_nocgo_linux.go b/src/crypto/systemcrypto_nocgo_linux.go -new file mode 100644 -index 00000000000000..7500bd3a86472b ---- /dev/null -+++ b/src/crypto/systemcrypto_nocgo_linux.go -@@ -0,0 +1,18 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto && !(cgo || 386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64 || s390x) -+ -+package crypto -+ -+func init() { -+ ` -+ Using GOEXPERIMENT=systemcrypto on Linux requires CGO_ENABLED=1. -+ -+ Our cgo-less OpenSSL implementation is only supported on certain architectures. -+ These architectures are: 386, amd64, arm, arm64, loong64, ppc64le, riscv64 and s390x. -+ -+ For more information, including how to request support for additional architectures, visit https://github.com/microsoft/go/blob/microsoft/main/eng/doc/MigrationGuide.md#cgo-is-not-enabled -+ ` -+} -diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index 504426b5fe97f2..a8aee3a4bccda3 100644 ---- a/src/go/build/deps_test.go -+++ b/src/go/build/deps_test.go -@@ -368,7 +368,7 @@ var depsRules = ` - math/big, go/token - < go/constant; - -- FMT, internal/goexperiment -+ FMT, internal/goexperiment, internal/platform - < internal/buildcfg; - - # The vast majority of standard library packages should not be resorting to regexp. -@@ -553,6 +553,10 @@ var depsRules = ` - < github.com/microsoft/go-crypto-winnative/internal/sysdll - < github.com/microsoft/go-crypto-winnative/internal/bcrypt; - -+ github.com/microsoft/go-crypto-openssl/osslsetup -+ < crypto/internal/backend/internal/opensslsetup -+ < crypto/internal/backend/fips140; -+ - FIPS, internal/godebug, embed - < crypto/internal/fips140only - < crypto -@@ -573,6 +577,13 @@ var depsRules = ` - github.com/microsoft/go-crypto-winnative/internal/bcrypt - < github.com/microsoft/go-crypto-winnative/cng; - -+ github.com/microsoft/go-crypto-openssl/openssl, -+ github.com/microsoft/go-crypto-winnative/cng, -+ github.com/microsoft/go-crypto-darwin/xcrypto, -+ crypto/internal/backend/fips140, -+ crypto/internal/boring/sig -+ < crypto/internal/backend; -+ - FIPS, internal/godebug, embed, - crypto/internal/boring/sig, - crypto/internal/boring/syso, -@@ -612,8 +623,15 @@ var depsRules = ` - math/big, github.com/microsoft/go-crypto-darwin/xcrypto < github.com/microsoft/go-crypto-darwin/bbig; - math/big, github.com/microsoft/go-crypto-winnative/cng < github.com/microsoft/go-crypto-winnative/cng/bbig; - -- CRYPTO, FMT, math/big, internal/saferio -- < crypto/internal/boring/bbig -+ FMT, math/big, crypto/internal/boring -+ < crypto/internal/boring/bbig; -+ -+ CRYPTO, FMT, math/big, internal/saferio, -+ crypto/internal/boring/bbig, -+ github.com/microsoft/go-crypto-openssl/bbig, -+ github.com/microsoft/go-crypto-darwin/bbig, -+ github.com/microsoft/go-crypto-winnative/cng/bbig -+ < crypto/internal/backend/bbig - < crypto/internal/fips140cache - < crypto/rand - < crypto/ed25519 # depends on crypto/rand.Reader -diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go -index d474ed1d4d466d..864f3ab2f05a66 100644 ---- a/src/internal/buildcfg/exp.go -+++ b/src/internal/buildcfg/exp.go -@@ -8,9 +8,11 @@ import ( - "fmt" - "os" - "reflect" -+ "slices" - "strings" - - "internal/goexperiment" -+ goplatform "internal/platform" - ) - - // ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline -@@ -81,17 +83,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { - regabiSupported = true - } - -- // These are the goos/goarch combinations where systemcrypto is -- // enabled by default. -- var systemCryptoSupported bool -- switch goos { -- case "windows": -- systemCryptoSupported = goarch == "amd64" || goarch == "arm64" -- case "linux": -- systemCryptoSupported = true -- case "darwin": -- systemCryptoSupported = true -- } -+ systemCryptoSupported := goplatform.SystemCryptoSupported(goos, goarch) - - // Older versions (anything before V16) of dsymutil don't handle - // the .debug_rnglists section in DWARF5. See -@@ -162,6 +154,16 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { - if strings.HasPrefix(f, "no") { - f, val = f[2:], false - } -+ // Check for deprecated per-platform crypto experiments. -+ switch f { -+ case "opensslcrypto", "cngcrypto", "darwincrypto": -+ switch { -+ case goos == "darwin" || goos == "linux" || (goos == "windows" && goarch != "386"): -+ return nil, fmt.Errorf("systemcrypto is enabled by default, GOEXPERIMENT %s is not necessary", f) -+ default: -+ return nil, fmt.Errorf("GOEXPERIMENT %s has been removed, use GOEXPERIMENT=systemcrypto instead", f) -+ } -+ } - set, ok := names[f] - if !ok { - return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f) -@@ -186,6 +188,10 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { - if flags.BoringCrypto { - return nil, fmt.Errorf("GOEXPERIMENT boringcrypto is not supported in the Microsoft build of Go") - } -+ if flags.SystemCrypto && !systemCryptoSupported && goplatform.BuildModeSupported("gc", "default", goos, goarch) { -+ // System crypto enabled but not supported. -+ return nil, fmt.Errorf("GOEXPERIMENT systemcrypto is not supported on %s/%s\n\tFor more information, visit https://github.com/microsoft/go/tree/microsoft/main/eng/doc/fips", goos, goarch) -+ } - return flags, nil - } - -@@ -195,6 +201,26 @@ func (exp *ExperimentFlags) String() string { - return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",") - } - -+// MSString returns the canonical GOEXPERIMENT string to enable this experiment -+// configuration. This is a modified version of what's returned by -+// [ExperimentFlags.String] that ensures experiments that have a different -+// baseline in the Microsoft build of Go are elided. -+// -+// Elision is important because this method is involved in the value of -+// runtime.Version(), and many usages out there, e.g. the standard library -+// go/version package, don't expect the version to contain experiments. -+func (exp *ExperimentFlags) MSString() string { -+ // Parse [String] rather than calling expList directly so that changes to -+ // [String] are reflected in [MSString] without careful tracking. -+ exps := strings.Split(exp.String(), ",") -+ // The Microsoft build of Go default behavior sets a few experiments that we -+ // need to remove from the result. -+ exps = slices.DeleteFunc(exps, func(s string) bool { -+ return s == "nosystemcrypto" -+ }) -+ return strings.Join(exps, ",") -+} -+ - // expList returns the list of lower-cased experiment names for - // experiments that differ from base. base may be nil to indicate no - // experiments. If all is true, then include all experiment flags, -diff --git a/src/runtime/runtime_boring.go b/src/runtime/runtime_boring.go -index 831ee67afc952d..d2f00d57c10286 100644 ---- a/src/runtime/runtime_boring.go -+++ b/src/runtime/runtime_boring.go -@@ -14,3 +14,8 @@ func boring_runtime_arg0() string { - } - return argslice[0] - } -+ -+//go:linkname crypto_backend_runtime_arg0 crypto/internal/backend.runtime_arg0 -+func crypto_backend_runtime_arg0() string { -+ return boring_runtime_arg0() -+} diff --git a/patches/0006-Add-support-for-logging-used-Windows-APIs.patch b/patches/0004-Add-support-for-logging-used-Windows-APIs.patch similarity index 100% rename from patches/0006-Add-support-for-logging-used-Windows-APIs.patch rename to patches/0004-Add-support-for-logging-used-Windows-APIs.patch diff --git a/patches/0004-Use-crypto-backends.patch b/patches/0004-Use-crypto-backends.patch deleted file mode 100644 index 131d7d110a6..00000000000 --- a/patches/0004-Use-crypto-backends.patch +++ /dev/null @@ -1,4501 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: bot-for-go[bot] <199222863+bot-for-go[bot]@users.noreply.github.com> -Date: Tue, 14 Jan 2025 11:10:21 +0100 -Subject: [PATCH] Use crypto backends - ---- - src/cmd/api/boring_test.go | 2 +- - src/cmd/dist/test.go | 5 +- - src/cmd/go/go_boring_test.go | 6 +- - src/cmd/go/script_test.go | 2 + - src/cmd/go/testdata/script/darwin_no_cgo.txt | 1 + - .../go/testdata/script/gopath_std_vendor.txt | 9 + - src/cmd/link/internal/ld/config.go | 8 + - src/cmd/link/internal/ld/lib.go | 6 + - src/crypto/aes/aes.go | 2 +- - src/crypto/aes/aes_test.go | 2 +- - src/crypto/boring/boring.go | 4 +- - src/crypto/cipher/cbc.go | 16 ++ - src/crypto/cipher/ctr.go | 7 + - src/crypto/cipher/ctr_aes_test.go | 2 +- - src/crypto/cipher/gcm.go | 59 +++++- - src/crypto/cipher/gcm_test.go | 9 +- - src/crypto/des/cipher.go | 7 + - src/crypto/dsa/boring.go | 113 ++++++++++ - src/crypto/dsa/dsa.go | 88 ++++++++ - src/crypto/dsa/dsa_test.go | 8 + - src/crypto/dsa/notboring.go | 16 ++ - src/crypto/ecdh/ecdh.go | 2 +- - src/crypto/ecdh/ecdh_test.go | 17 +- - src/crypto/ecdh/nist.go | 30 +-- - src/crypto/ecdh/x25519.go | 49 +++++ - src/crypto/ecdsa/badlinkname.go | 19 ++ - src/crypto/ecdsa/boring.go | 6 +- - src/crypto/ecdsa/ecdsa.go | 10 +- - src/crypto/ecdsa/notboring.go | 4 +- - src/crypto/ed25519/boring.go | 71 +++++++ - src/crypto/ed25519/ed25519.go | 70 +++++++ - src/crypto/ed25519/ed25519_test.go | 14 +- - src/crypto/ed25519/notboring.go | 16 ++ - src/crypto/fips140/enforcement_test.go | 4 + - src/crypto/fips140/fips140.go | 3 +- - src/crypto/hkdf/hkdf.go | 14 ++ - src/crypto/hkdf/hkdf_test.go | 2 +- - src/crypto/hmac/hmac.go | 16 +- - src/crypto/hmac/hmac_test.go | 2 +- - src/crypto/hpke/aead.go | 14 +- - src/crypto/internal/cryptotest/allocations.go | 6 - - src/crypto/internal/cryptotest/fips140.go | 8 + - src/crypto/internal/cryptotest/hash.go | 3 +- - .../internal/cryptotest/implementations.go | 2 +- - src/crypto/internal/fips140hash/hash.go | 3 +- - .../internal/fips140only/fips140only_test.go | 45 ++-- - src/crypto/internal/fips140test/acvp_test.go | 6 + - src/crypto/internal/fips140test/cast_test.go | 2 + - src/crypto/internal/fips140test/fips_test.go | 2 +- - src/crypto/internal/rand/rand.go | 2 +- - src/crypto/md5/md5.go | 10 + - src/crypto/md5/md5_test.go | 18 +- - src/crypto/mlkem/mlkem.go | 118 +++++++++-- - src/crypto/mlkem/mlkem_test.go | 8 + - src/crypto/pbkdf2/pbkdf2.go | 7 + - src/crypto/pbkdf2/pbkdf2_test.go | 6 +- - src/crypto/purego_test.go | 2 +- - src/crypto/rand/rand.go | 2 +- - src/crypto/rand/rand_test.go | 9 +- - src/crypto/rc4/rc4.go | 18 ++ - src/crypto/rsa/boring.go | 13 +- - src/crypto/rsa/boring_test.go | 2 +- - src/crypto/rsa/fips.go | 94 +++++---- - src/crypto/rsa/notboring.go | 4 +- - src/crypto/rsa/pkcs1v15.go | 10 +- - src/crypto/rsa/pkcs1v15_test.go | 5 + - src/crypto/rsa/pss_test.go | 14 +- - src/crypto/rsa/rsa.go | 29 +-- - src/crypto/rsa/rsa_darwin.go | 71 +++++++ - src/crypto/rsa/rsa_test.go | 7 +- - src/crypto/sha1/sha1.go | 11 +- - src/crypto/sha1/sha1_test.go | 11 +- - src/crypto/sha256/sha256.go | 6 +- - src/crypto/sha256/sha256_test.go | 44 ++-- - src/crypto/sha3/sha3.go | 129 ++++++++++-- - src/crypto/sha3/sha3_test.go | 18 +- - src/crypto/sha512/sha512.go | 14 +- - src/crypto/sha512/sha512_test.go | 32 ++- - src/crypto/tls/cipher_suites.go | 10 +- - src/crypto/tls/fipsonly/fipsonly.go | 2 +- - src/crypto/tls/fipsonly/fipsonly_test.go | 2 +- - src/crypto/tls/handshake_client.go | 17 +- - src/crypto/tls/handshake_client_tls13.go | 2 +- - src/crypto/tls/handshake_server.go | 15 +- - src/crypto/tls/handshake_server_tls13.go | 5 +- - src/crypto/tls/internal/tls13/doc.go | 18 ++ - src/crypto/tls/internal/tls13/tls13.go | 195 ++++++++++++++++++ - src/crypto/tls/key_schedule.go | 2 +- - src/crypto/tls/prf.go | 24 +++ - src/crypto/tls/prf_test.go | 9 + - src/crypto/x509/verify_test.go | 2 +- - src/go/build/deps_test.go | 13 +- - src/hash/boring_test.go | 9 + - src/hash/example_test.go | 2 + - src/hash/marshal_test.go | 4 + - src/hash/notboring_test.go | 9 + - src/net/lookup_test.go | 3 + - src/os/exec/exec_test.go | 9 + - 98 files changed, 1627 insertions(+), 251 deletions(-) - create mode 100644 src/crypto/dsa/boring.go - create mode 100644 src/crypto/dsa/notboring.go - create mode 100644 src/crypto/ecdsa/badlinkname.go - create mode 100644 src/crypto/ed25519/boring.go - create mode 100644 src/crypto/ed25519/notboring.go - create mode 100644 src/crypto/rsa/rsa_darwin.go - create mode 100644 src/crypto/tls/internal/tls13/doc.go - create mode 100644 src/crypto/tls/internal/tls13/tls13.go - create mode 100644 src/hash/boring_test.go - create mode 100644 src/hash/notboring_test.go - -diff --git a/src/cmd/api/boring_test.go b/src/cmd/api/boring_test.go -index f0e3575637c62a..9eab3b4e66e60b 100644 ---- a/src/cmd/api/boring_test.go -+++ b/src/cmd/api/boring_test.go -@@ -2,7 +2,7 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - package main - -diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go -index dab28b6f1ced0a..3263a1700e5980 100644 ---- a/src/cmd/dist/test.go -+++ b/src/cmd/dist/test.go -@@ -722,7 +722,7 @@ func (t *tester) registerTests() { - }) - - // Check that all crypto packages compile (and test correctly, in longmode) with fips. -- if t.fipsSupported() { -+ if false { // Disable these tests, they don't work if CNG/OpenSSL FIPS mode is not enabled. We already have dedicated builders for this. - // Test standard crypto packages with fips140=on. - t.registerTest("GOFIPS140=latest go test crypto/...", &goTest{ - variant: "gofips140", -@@ -1386,12 +1386,11 @@ func (t *tester) registerCgoTests(heading string) { - // a C linker warning on Linux. - // in function `bio_ip_and_port_to_socket_and_addr': - // warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking -- if staticCheck.skip == nil && goos == "linux" && strings.Contains(goexperiment, "boringcrypto") { -+ if staticCheck.skip == nil && goos == "linux" && strings.Contains(goexperiment, "systemcrypto") { - staticCheck.skip = func(*distTest) (string, bool) { - return "skipping static linking check on Linux when using boringcrypto to avoid C linker warning about getaddrinfo", true - } - } -- - // Static linking tests - if goos != "android" && p != "netbsd/arm" && !t.msan && !t.asan { - // TODO(#56629): Why does this fail on netbsd-arm? -diff --git a/src/cmd/go/go_boring_test.go b/src/cmd/go/go_boring_test.go -index ed0fbf3d53d75b..a2f74c8095d449 100644 ---- a/src/cmd/go/go_boring_test.go -+++ b/src/cmd/go/go_boring_test.go -@@ -2,11 +2,13 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - package main_test - --import "testing" -+import ( -+ "testing" -+) - - func TestBoringInternalLink(t *testing.T) { - tg := testgo(t) -diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go -index 088b9a8b5fdb2e..6a27473d4ed1d6 100644 ---- a/src/cmd/go/script_test.go -+++ b/src/cmd/go/script_test.go -@@ -299,6 +299,8 @@ var extraEnvKeys = []string{ - "GO_TESTING_GOTOOLS", // for gccgo testing - "GCCGO", // for gccgo testing - "GCCGOTOOLDIR", // for gccgo testing -+ -+ "OPENSSL_FORCE_FIPS_MODE", // useful for testing on Mariner 2. - } - - // updateSum runs 'go mod tidy', 'go list -mod=mod -m all', or -diff --git a/src/cmd/go/testdata/script/darwin_no_cgo.txt b/src/cmd/go/testdata/script/darwin_no_cgo.txt -index fa445925b7c374..e4b9714df817ea 100644 ---- a/src/cmd/go/testdata/script/darwin_no_cgo.txt -+++ b/src/cmd/go/testdata/script/darwin_no_cgo.txt -@@ -4,6 +4,7 @@ - # of cmd/go, which imports approximately everything - # in std (certainly everything relevant). - [!GOOS:darwin] skip -+[GOEXPERIMENT:systemcrypto] skip - go list -deps cmd/go - ! stdout runtime/cgo - -diff --git a/src/cmd/go/testdata/script/gopath_std_vendor.txt b/src/cmd/go/testdata/script/gopath_std_vendor.txt -index 4aaf46b5d0f0dc..ec58a217400caa 100644 ---- a/src/cmd/go/testdata/script/gopath_std_vendor.txt -+++ b/src/cmd/go/testdata/script/gopath_std_vendor.txt -@@ -1,5 +1,14 @@ - env GO111MODULE=off - -+# Disable systemcrypto while evaluating test dependencies to avoid importing -+# vendored crypto module dependencies like go-crypto-openssl. This test script -+# is not set up to handle any vendored libraries being imported other than -+# golang.org/x/net/http2/hpack, so we must make sure it is the only one. -+# -+# See https://github.com/microsoft/go/issues/481 for more details, such as the -+# dependency chain that would cause the failure if the gocrypt tag isn't used. -+env GOEXPERIMENT=none -+ - [!compiler:gc] skip - - go list -f '{{.Dir}}' vendor/golang.org/x/net/http2/hpack -diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go -index acbebf28598a9c..5f6d8a91ea384c 100644 ---- a/src/cmd/link/internal/ld/config.go -+++ b/src/cmd/link/internal/ld/config.go -@@ -7,6 +7,7 @@ package ld - import ( - "fmt" - "internal/buildcfg" -+ "internal/goexperiment" - "internal/platform" - ) - -@@ -34,6 +35,13 @@ func (mode *BuildMode) Set(s string) error { - return fmt.Errorf("invalid buildmode: %q", s) - case "exe": - switch buildcfg.GOOS + "/" + buildcfg.GOARCH { -+ case "darwin/amd64": -+ // We can't link against the static object file when using no_pie -+ if goexperiment.SystemCrypto { -+ *mode = BuildModePIE -+ } else { -+ *mode = BuildModeExe -+ } - case "darwin/arm64", "windows/arm64": // On these platforms, everything is PIE - *mode = BuildModePIE - default: -diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go -index 9cbc12919f1d00..e64a9c5299e95a 100644 ---- a/src/cmd/link/internal/ld/lib.go -+++ b/src/cmd/link/internal/ld/lib.go -@@ -1177,6 +1177,12 @@ var hostobj []Hostobj - // These packages can use internal linking mode. - // Others trigger external mode. - var internalpkg = []string{ -+ "vendor/github.com/microsoft/go-crypto-darwin/internal/cryptokit", -+ "vendor/github.com/microsoft/go-crypto-darwin/internal/commoncrypto", -+ "vendor/github.com/microsoft/go-crypto-darwin/internal/security", -+ "vendor/github.com/microsoft/go-crypto-openssl/internal/ossl", -+ "vendor/github.com/microsoft/go-crypto-openssl/osslsetup", -+ "vendor/github.com/microsoft/go-crypto-openssl/openssl", - "crypto/internal/boring", - "crypto/internal/boring/syso", - "crypto/x509", -diff --git a/src/crypto/aes/aes.go b/src/crypto/aes/aes.go -index 22ea8819ed239a..1e2cba08c1c760 100644 ---- a/src/crypto/aes/aes.go -+++ b/src/crypto/aes/aes.go -@@ -15,7 +15,7 @@ package aes - - import ( - "crypto/cipher" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/aes" - "strconv" - ) -diff --git a/src/crypto/aes/aes_test.go b/src/crypto/aes/aes_test.go -index cfe75f4057a96b..19a5e7d757ca8c 100644 ---- a/src/crypto/aes/aes_test.go -+++ b/src/crypto/aes/aes_test.go -@@ -5,7 +5,7 @@ - package aes - - import ( -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "fmt" - "testing" -diff --git a/src/crypto/boring/boring.go b/src/crypto/boring/boring.go -index 097c37e343fdb8..a5d603896d3890 100644 ---- a/src/crypto/boring/boring.go -+++ b/src/crypto/boring/boring.go -@@ -2,7 +2,7 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - // Package boring exposes functions that are only available when building with - // Go+BoringCrypto. This package is available on all targets as long as the -@@ -13,7 +13,7 @@ - // is satisfied, so that applications can tag files that use this package. - package boring - --import "crypto/internal/boring" -+import boring "crypto/internal/backend" - - // Enabled reports whether BoringCrypto handles supported crypto operations. - func Enabled() bool { -diff --git a/src/crypto/cipher/cbc.go b/src/crypto/cipher/cbc.go -index 87bafee08ade4f..198de1d17b7e7c 100644 ---- a/src/crypto/cipher/cbc.go -+++ b/src/crypto/cipher/cbc.go -@@ -44,6 +44,12 @@ type cbcEncAble interface { - NewCBCEncrypter(iv []byte) BlockMode - } - -+// fipsCBCEncAble is an interface implemented by ciphers that have a specific -+// optimized implementation of CBC encryption that is compliant with FIPS 140-2. -+type fipsCBCEncAble interface { -+ NewFIPSCBCEncrypter(iv []byte) BlockMode -+} -+ - // NewCBCEncrypter returns a BlockMode which encrypts in cipher block chaining - // mode, using the given Block. The length of iv must be the same as the - // Block's block size. -@@ -54,6 +60,9 @@ func NewCBCEncrypter(b Block, iv []byte) BlockMode { - if b, ok := b.(*aes.Block); ok { - return aes.NewCBCEncrypter(b, [16]byte(iv)) - } -+ if cbc, ok := b.(fipsCBCEncAble); ok { -+ return cbc.NewFIPSCBCEncrypter(iv) -+ } - if fips140only.Enforced() { - panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") - } -@@ -123,6 +132,10 @@ type cbcDecAble interface { - NewCBCDecrypter(iv []byte) BlockMode - } - -+type fipsCBCDecAble interface { -+ NewFIPSCBCDecrypter(iv []byte) BlockMode -+} -+ - // NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining - // mode, using the given Block. The length of iv must be the same as the - // Block's block size and must match the iv used to encrypt the data. -@@ -133,6 +146,9 @@ func NewCBCDecrypter(b Block, iv []byte) BlockMode { - if b, ok := b.(*aes.Block); ok { - return aes.NewCBCDecrypter(b, [16]byte(iv)) - } -+ if cbc, ok := b.(fipsCBCDecAble); ok { -+ return cbc.NewFIPSCBCDecrypter(iv) -+ } - if fips140only.Enforced() { - panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") - } -diff --git a/src/crypto/cipher/ctr.go b/src/crypto/cipher/ctr.go -index 8e63ed7e668a4c..10ed72d2540b6e 100644 ---- a/src/crypto/cipher/ctr.go -+++ b/src/crypto/cipher/ctr.go -@@ -36,12 +36,19 @@ type ctrAble interface { - NewCTR(iv []byte) Stream - } - -+type fipsCTRAble interface { -+ NewFIPSCTR(iv []byte) Stream -+} -+ - // NewCTR returns a [Stream] which encrypts/decrypts using the given [Block] in - // counter mode. The length of iv must be the same as the [Block]'s block size. - func NewCTR(block Block, iv []byte) Stream { - if block, ok := block.(*aes.Block); ok { - return aesCtrWrapper{aes.NewCTR(block, iv)} - } -+ if ctr, ok := block.(fipsCTRAble); ok { -+ return ctr.NewFIPSCTR(iv) -+ } - if fips140only.Enforced() { - panic("crypto/cipher: use of CTR with non-AES ciphers is not allowed in FIPS 140-only mode") - } -diff --git a/src/crypto/cipher/ctr_aes_test.go b/src/crypto/cipher/ctr_aes_test.go -index b6620d182fefc7..d89be99cf57545 100644 ---- a/src/crypto/cipher/ctr_aes_test.go -+++ b/src/crypto/cipher/ctr_aes_test.go -@@ -14,7 +14,7 @@ import ( - "bytes" - "crypto/aes" - "crypto/cipher" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - fipsaes "crypto/internal/fips140/aes" - "encoding/binary" -diff --git a/src/crypto/cipher/gcm.go b/src/crypto/cipher/gcm.go -index f2cf6248dc8c9b..663d418c4e5631 100644 ---- a/src/crypto/cipher/gcm.go -+++ b/src/crypto/cipher/gcm.go -@@ -89,6 +89,15 @@ func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) { - func NewGCMWithRandomNonce(cipher Block) (AEAD, error) { - c, ok := cipher.(*aes.Block) - if !ok { -+ if cipher, ok := cipher.(gcmAble); ok { -+ g, err := cipher.NewGCM(gcmStandardNonceSize, gcmTagSize) -+ if err != nil { -+ return nil, err -+ } -+ if _, ok := g.(randomNonceSealer); ok { -+ return gcmWithRandomNonce{g}, nil -+ } -+ } - return nil, errors.New("cipher: NewGCMWithRandomNonce requires aes.Block") - } - g, err := gcm.New(c, gcmStandardNonceSize, gcmTagSize) -@@ -99,7 +108,7 @@ func NewGCMWithRandomNonce(cipher Block) (AEAD, error) { - } - - type gcmWithRandomNonce struct { -- *gcm.GCM -+ aead AEAD - } - - func (g gcmWithRandomNonce) NonceSize() int { -@@ -153,7 +162,14 @@ func (g gcmWithRandomNonce) Seal(dst, nonce, plaintext, additionalData []byte) [ - plaintext = ciphertext[:len(plaintext)] - } - -- gcm.SealWithRandomNonce(g.GCM, nonce, ciphertext, plaintext, additionalData) -+ if aeadGCM, ok := g.aead.(*gcm.GCM); ok { -+ gcm.SealWithRandomNonce(aeadGCM, nonce, ciphertext, plaintext, additionalData) -+ } else if aeadGCM, ok := g.aead.(randomNonceSealer); ok { -+ aeadGCM.SealWithRandomNonce(ciphertext, nonce, plaintext, additionalData) -+ } else { -+ panic("cipher: NewGCMWithRandomNonce requires aes.Block") -+ } -+ - return ret - } - -@@ -186,7 +202,7 @@ func (g gcmWithRandomNonce) Open(dst, nonce, ciphertext, additionalData []byte) - ciphertext = ciphertext[gcmStandardNonceSize:] - } - -- _, err := g.GCM.Open(out[:0], nonce, ciphertext, additionalData) -+ _, err := g.aead.Open(out[:0], nonce, ciphertext, additionalData) - if err != nil { - return nil, err - } -@@ -200,6 +216,14 @@ type gcmAble interface { - NewGCM(nonceSize, tagSize int) (AEAD, error) - } - -+// randomNonceSealer is an interface for AEAD implementations that support -+// sealing messages using a randomly generated nonce. -+// It is used by NewGCMWithRandomNonce to determine whether the AEAD instance -+// provides its own implementation of SealWithRandomNonce. -+type randomNonceSealer interface { -+ SealWithRandomNonce(out, nonce, plaintext, additionalData []byte) -+} -+ - func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) { - if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize { - return nil, errors.New("cipher: incorrect tag size given to GCM") -@@ -208,7 +232,16 @@ func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) { - return nil, errors.New("cipher: the nonce can't have zero length") - } - if cipher, ok := cipher.(gcmAble); ok { -- return cipher.NewGCM(nonceSize, tagSize) -+ g, err := cipher.NewGCM(nonceSize, tagSize) -+ if err != nil { -+ return nil, err -+ } -+ -+ if _, ok := g.(randomNonceSealer); ok { -+ return customGCMWrapper{g}, nil -+ } -+ -+ return g, err - } - if cipher.BlockSize() != gcmBlockSize { - return nil, errors.New("cipher: NewGCM requires 128-bit block cipher") -@@ -216,6 +249,24 @@ func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) { - return &gcmFallback{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize}, nil - } - -+// customGCMWrapper wraps an AEAD implementation to expose only the AEAD interface methods. -+// This prevents extra exported methods from causing test failures in cryptotest.NoExtraMethods. -+type customGCMWrapper struct { -+ aead AEAD -+} -+ -+func (g customGCMWrapper) NonceSize() int { return g.aead.NonceSize() } -+ -+func (g customGCMWrapper) Overhead() int { return g.aead.Overhead() } -+ -+func (g customGCMWrapper) Seal(dst, nonce, plaintext, additionalData []byte) []byte { -+ return g.aead.Seal(dst, nonce, plaintext, additionalData) -+} -+ -+func (g customGCMWrapper) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { -+ return g.aead.Open(dst, nonce, ciphertext, additionalData) -+} -+ - // gcmFallback is only used for non-AES ciphers, which regrettably we - // theoretically support. It's a copy of the generic implementation from - // crypto/internal/fips140/aes/gcm/gcm_generic.go, refer to that file for more details. -diff --git a/src/crypto/cipher/gcm_test.go b/src/crypto/cipher/gcm_test.go -index 8ba0bb3981319d..7b212f8f19b101 100644 ---- a/src/crypto/cipher/gcm_test.go -+++ b/src/crypto/cipher/gcm_test.go -@@ -8,7 +8,6 @@ import ( - "bytes" - "crypto/aes" - "crypto/cipher" -- "crypto/internal/boring" - "crypto/internal/cryptotest" - "crypto/internal/fips140" - fipsaes "crypto/internal/fips140/aes" -@@ -727,9 +726,13 @@ func testGCMAEAD(t *testing.T, newCipher func(key []byte) cipher.Block) { - - // Test NewGCMWithRandomNonce. - t.Run("GCMWithRandomNonce", func(t *testing.T) { -- if _, ok := block.(*wrapper); ok || boring.Enabled { -+ if _, ok := block.(*wrapper); ok { - t.Skip("NewGCMWithRandomNonce requires an AES block cipher") - } -+ a, _ := cipher.NewGCMWithRandomNonce(block) -+ if a.NonceSize() != 0 { -+ t.Errorf("NewGCMWithRandomNonce returned AEAD with non-zero nonce size: %d", a.NonceSize()) -+ } - cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCMWithRandomNonce(block) }) - }) - }) -@@ -752,7 +755,7 @@ func TestGCMExtraMethods(t *testing.T) { - }) - t.Run("NewGCMWithRandomNonce", func(t *testing.T) { - block := newCipher(make([]byte, 16)) -- if _, ok := block.(*wrapper); ok || boring.Enabled { -+ if _, ok := block.(*wrapper); ok { - t.Skip("NewGCMWithRandomNonce requires an AES block cipher") - } - a, _ := cipher.NewGCMWithRandomNonce(block) -diff --git a/src/crypto/des/cipher.go b/src/crypto/des/cipher.go -index 81ba766b2527d2..a0fc16647c5b5f 100644 ---- a/src/crypto/des/cipher.go -+++ b/src/crypto/des/cipher.go -@@ -6,6 +6,7 @@ package des - - import ( - "crypto/cipher" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/alias" - "crypto/internal/fips140only" - "errors" -@@ -36,6 +37,9 @@ func NewCipher(key []byte) (cipher.Block, error) { - if len(key) != 8 { - return nil, KeySizeError(len(key)) - } -+ if boring.Enabled && boring.SupportsDESCipher() { -+ return boring.NewDESCipher(key) -+ } - - c := new(desCipher) - c.generateSubkeys(key) -@@ -84,6 +88,9 @@ func NewTripleDESCipher(key []byte) (cipher.Block, error) { - if len(key) != 24 { - return nil, KeySizeError(len(key)) - } -+ if boring.Enabled && boring.SupportsTripleDESCipher() { -+ return boring.NewTripleDESCipher(key) -+ } - - c := new(tripleDESCipher) - c.cipher1.generateSubkeys(key[:8]) -diff --git a/src/crypto/dsa/boring.go b/src/crypto/dsa/boring.go -new file mode 100644 -index 00000000000000..7ea0c8c423e9ee ---- /dev/null -+++ b/src/crypto/dsa/boring.go -@@ -0,0 +1,113 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package dsa -+ -+import ( -+ boring "crypto/internal/backend" -+ "crypto/internal/backend/bbig" -+ "crypto/internal/boring/bcache" -+ "math/big" -+) -+ -+// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. -+// -+// The first operation on a PublicKey or PrivateKey makes a parallel -+// BoringCrypto key and saves it in pubCache or privCache. -+// -+// We could just assume that once used in a sign/verify/encrypt/decrypt operation, -+// a particular key is never again modified, but that has not been a -+// stated assumption before. Just in case there is any existing code that -+// does modify the key between operations, we save the original values -+// alongside the cached BoringCrypto key and check that the real key -+// still matches before using the cached key. The theory is that the real -+// operations are significantly more expensive than the comparison. -+ -+type boringPub struct { -+ key *boring.PublicKeyDSA -+ orig PublicKey -+} -+ -+var pubCache bcache.Cache[PublicKey, boringPub] -+var privCache bcache.Cache[PrivateKey, boringPriv] -+ -+func init() { -+ pubCache.Register() -+ privCache.Register() -+} -+ -+func boringPublicKey(pub *PublicKey) (*boring.PublicKeyDSA, error) { -+ b := pubCache.Get(pub) -+ if b != nil && publicKeyEqual(&b.orig, pub) { -+ return b.key, nil -+ } -+ -+ b = new(boringPub) -+ b.orig = copyPublicKey(pub) -+ key, err := boring.NewPublicKeyDSA(bbig.Enc(b.orig.P), bbig.Enc(b.orig.Q), bbig.Enc(b.orig.G), bbig.Enc(b.orig.Y)) -+ if err != nil { -+ return nil, err -+ } -+ b.key = key -+ pubCache.Put(pub, b) -+ return key, nil -+} -+ -+type boringPriv struct { -+ key *boring.PrivateKeyDSA -+ orig PrivateKey -+} -+ -+func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyDSA, error) { -+ b := privCache.Get(priv) -+ if b != nil && privateKeyEqual(&b.orig, priv) { -+ return b.key, nil -+ } -+ -+ b = new(boringPriv) -+ b.orig = copyPrivateKey(priv) -+ -+ P := b.orig.P -+ Q := b.orig.Q -+ G := b.orig.G -+ X := b.orig.X -+ Y := b.orig.Y -+ -+ key, err := boring.NewPrivateKeyDSA(bbig.Enc(P), bbig.Enc(Q), bbig.Enc(G), bbig.Enc(X), bbig.Enc(Y)) -+ if err != nil { -+ return nil, err -+ } -+ b.key = key -+ privCache.Put(priv, b) -+ return key, nil -+} -+ -+func publicKeyEqual(k1, k2 *PublicKey) bool { -+ return k1.Y != nil && k1.Y.Cmp(k2.Y) == 0 && k1.P.Cmp(k2.P) == 0 && k1.Q.Cmp(k2.Q) == 0 && k1.G.Cmp(k2.G) == 0 -+} -+ -+func copyPublicKey(k *PublicKey) PublicKey { -+ return PublicKey{ -+ Parameters: Parameters{ -+ P: new(big.Int).Set(k.P), -+ Q: new(big.Int).Set(k.Q), -+ G: new(big.Int).Set(k.G), -+ }, -+ Y: new(big.Int).Set(k.Y), -+ } -+} -+ -+func privateKeyEqual(k1, k2 *PrivateKey) bool { -+ return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) && -+ k1.X.Cmp(k2.X) == 0 -+} -+ -+func copyPrivateKey(k *PrivateKey) PrivateKey { -+ return PrivateKey{ -+ PublicKey: copyPublicKey(&k.PublicKey), -+ X: new(big.Int).Set(k.X), -+ } -+} -diff --git a/src/crypto/dsa/dsa.go b/src/crypto/dsa/dsa.go -index 6724f861b7f2f0..4bda65558882a2 100644 ---- a/src/crypto/dsa/dsa.go -+++ b/src/crypto/dsa/dsa.go -@@ -18,8 +18,13 @@ import ( - "io" - "math/big" - -+ boring "crypto/internal/backend" -+ "crypto/internal/backend/bbig" - "crypto/internal/fips140only" - "crypto/internal/rand" -+ -+ "golang.org/x/crypto/cryptobyte" -+ "golang.org/x/crypto/cryptobyte/asn1" - ) - - // Parameters represents the domain parameters for a key. These parameters can -@@ -91,6 +96,17 @@ func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes - return errors.New("crypto/dsa: invalid ParameterSizes") - } - -+ if boring.Enabled && boring.SupportsDSA(L, N) { -+ p, q, g, err := boring.GenerateParametersDSA(L, N) -+ if err != nil { -+ return err -+ } -+ params.P = bbig.Dec(p) -+ params.Q = bbig.Dec(q) -+ params.G = bbig.Dec(g) -+ return nil -+ } -+ - qBytes := make([]byte, N/8) - pBytes := make([]byte, L/8) - -@@ -170,6 +186,17 @@ func GenerateKey(priv *PrivateKey, rand io.Reader) error { - return errors.New("crypto/dsa: parameters not set up before generating key") - } - -+ if boring.Enabled && boring.SupportsDSA(priv.P.BitLen(), priv.Q.BitLen()) { -+ x, y, err := boring.GenerateKeyDSA(bbig.Enc(priv.P), bbig.Enc(priv.Q), bbig.Enc(priv.G)) -+ if err != nil { -+ return err -+ } -+ priv.X = bbig.Dec(x) -+ priv.Y = bbig.Dec(y) -+ -+ return nil -+ } -+ - x := new(big.Int) - xBytes := make([]byte, priv.Q.BitLen()/8) - -@@ -229,6 +256,18 @@ func Sign(random io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err e - err = ErrInvalidPublicKey - return - } -+ -+ if boring.Enabled && boring.SupportsDSA(priv.P.BitLen(), priv.Q.BitLen()) { -+ b, err := boringPrivateKey(priv) -+ if err != nil { -+ return nil, nil, err -+ } -+ -+ r, s, err := boring.SignDSA(b, hash, parseSignature) -+ -+ return bbig.Dec(r), bbig.Dec(s), err -+ } -+ - n >>= 3 - - var attempts int -@@ -292,6 +331,14 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { - panic("crypto/dsa: use of DSA is not allowed in FIPS 140-only mode") - } - -+ if boring.Enabled && boring.SupportsDSA(pub.P.BitLen(), pub.Q.BitLen()) { -+ bkey, err := boringPublicKey(pub) -+ if err != nil { -+ return false -+ } -+ -+ return boring.VerifyDSA(bkey, hash, bbig.Enc(r), bbig.Enc(s), encodeSignature) -+ } - // FIPS 186-3, section 4.7 - - if pub.P.Sign() == 0 { -@@ -328,3 +375,44 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { - - return v.Cmp(r) == 0 - } -+ -+func parseSignature(sig []byte) (boring.BigInt, boring.BigInt, error) { -+ var r, s []byte -+ var inner cryptobyte.String -+ input := cryptobyte.String(sig) -+ if !input.ReadASN1(&inner, asn1.SEQUENCE) || -+ !input.Empty() || -+ !inner.ReadASN1Integer(&r) || -+ !inner.ReadASN1Integer(&s) || -+ !inner.Empty() { -+ return nil, nil, errors.New("invalid ASN.1") -+ } -+ return bbig.Enc(new(big.Int).SetBytes(r)), bbig.Enc(new(big.Int).SetBytes(s)), nil -+} -+ -+func encodeSignature(r, s boring.BigInt) ([]byte, error) { -+ var b cryptobyte.Builder -+ b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { -+ addASN1IntBytes(b, bbig.Dec(r).Bytes()) -+ addASN1IntBytes(b, bbig.Dec(s).Bytes()) -+ }) -+ return b.Bytes() -+} -+ -+// addASN1IntBytes encodes in ASN.1 a positive integer represented as -+// a big-endian byte slice with zero or more leading zeroes. -+func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) { -+ for len(bytes) > 0 && bytes[0] == 0 { -+ bytes = bytes[1:] -+ } -+ if len(bytes) == 0 { -+ b.SetError(errors.New("invalid integer")) -+ return -+ } -+ b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) { -+ if bytes[0]&0x80 != 0 { -+ c.AddUint8(0) -+ } -+ c.AddBytes(bytes) -+ }) -+} -diff --git a/src/crypto/dsa/dsa_test.go b/src/crypto/dsa/dsa_test.go -index ad85eac0a7f0b1..bf0d5eb077b2b1 100644 ---- a/src/crypto/dsa/dsa_test.go -+++ b/src/crypto/dsa/dsa_test.go -@@ -5,8 +5,11 @@ - package dsa - - import ( -+ "crypto/fips140" -+ boring "crypto/internal/backend" - "crypto/rand" - "math/big" -+ "runtime" - "testing" - ) - -@@ -83,6 +86,11 @@ func fromHex(s string) *big.Int { - } - - func TestSignAndVerify(t *testing.T) { -+ if boring.Enabled && runtime.GOOS == "linux" && fips140.Enabled() { -+ // Some OpenSSL providers don't support DSA signing in FIPS mode. -+ t.Skip("skipping DSA test in FIPS mode") -+ } -+ - priv := PrivateKey{ - PublicKey: PublicKey{ - Parameters: Parameters{ -diff --git a/src/crypto/dsa/notboring.go b/src/crypto/dsa/notboring.go -new file mode 100644 -index 00000000000000..cd02ff5a00c3dc ---- /dev/null -+++ b/src/crypto/dsa/notboring.go -@@ -0,0 +1,16 @@ -+// Copyright 2022 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !goexperiment.systemcrypto -+ -+package dsa -+ -+import boring "crypto/internal/backend" -+ -+func boringPublicKey(*PublicKey) (*boring.PublicKeyDSA, error) { -+ panic("boringcrypto: not available") -+} -+func boringPrivateKey(*PrivateKey) (*boring.PrivateKeyDSA, error) { -+ panic("boringcrypto: not available") -+} -diff --git a/src/crypto/ecdh/ecdh.go b/src/crypto/ecdh/ecdh.go -index 3f85a2833697a9..a5717e22db8d78 100644 ---- a/src/crypto/ecdh/ecdh.go -+++ b/src/crypto/ecdh/ecdh.go -@@ -8,7 +8,7 @@ package ecdh - - import ( - "crypto" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/ecdh" - "crypto/subtle" - "errors" -diff --git a/src/crypto/ecdh/ecdh_test.go b/src/crypto/ecdh/ecdh_test.go -index cfa919ade97ad0..40e316387173dc 100644 ---- a/src/crypto/ecdh/ecdh_test.go -+++ b/src/crypto/ecdh/ecdh_test.go -@@ -9,6 +9,7 @@ import ( - "crypto" - "crypto/cipher" - "crypto/ecdh" -+ boring "crypto/internal/backend" - "crypto/rand" - "crypto/sha256" - "encoding/hex" -@@ -207,13 +208,17 @@ func TestX25519Failure(t *testing.T) { - } - - func testX25519Failure(t *testing.T, private, public []byte) { -+ if boring.Enabled && !boring.SupportsCurve("X25519") { -+ t.Skip("backend does not support X25519") -+ } - priv, err := ecdh.X25519().NewPrivateKey(private) - if err != nil { - t.Fatal(err) - } - pub, err := ecdh.X25519().NewPublicKey(public) - if err != nil { -- t.Fatal(err) -+ // Some backends may reject the public key at import time, that's fine. -+ return - } - secret, err := priv.ECDH(pub) - if err == nil { -@@ -279,6 +284,9 @@ var invalidPrivateKeys = map[ecdh.Curve][]string{ - } - - func TestNewPrivateKey(t *testing.T) { -+ if boring.Enabled { -+ t.Skip("skipping test with different results when using different backends") -+ } - testAllCurves(t, func(t *testing.T, curve ecdh.Curve) { - for _, input := range invalidPrivateKeys[curve] { - k, err := curve.NewPrivateKey(hexDecode(t, input)) -@@ -364,7 +372,12 @@ func testAllCurves(t *testing.T, f func(t *testing.T, curve ecdh.Curve)) { - t.Run("P256", func(t *testing.T) { f(t, ecdh.P256()) }) - t.Run("P384", func(t *testing.T) { f(t, ecdh.P384()) }) - t.Run("P521", func(t *testing.T) { f(t, ecdh.P521()) }) -- t.Run("X25519", func(t *testing.T) { f(t, ecdh.X25519()) }) -+ t.Run("X25519", func(t *testing.T) { -+ if boring.Enabled && !boring.SupportsCurve("X25519") { -+ t.Skip("backend does not support X25519") -+ } -+ f(t, ecdh.X25519()) -+ }) - } - - func BenchmarkECDH(b *testing.B) { -diff --git a/src/crypto/ecdh/nist.go b/src/crypto/ecdh/nist.go -index 13865ea7012989..fb46bf4822fb6a 100644 ---- a/src/crypto/ecdh/nist.go -+++ b/src/crypto/ecdh/nist.go -@@ -6,7 +6,7 @@ package ecdh - - import ( - "bytes" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/ecdh" - "crypto/internal/fips140only" - "crypto/internal/rand" -@@ -123,24 +123,26 @@ func (c *nistCurve) NewPublicKey(key []byte) (*PublicKey, error) { - if len(key) == 0 || key[0] != 4 { - return nil, errors.New("crypto/ecdh: invalid public key") - } -- k := &PublicKey{ -- curve: c, -- publicKey: bytes.Clone(key), -- } - if boring.Enabled { -- bk, err := boring.NewPublicKeyECDH(c.name, k.publicKey) -+ bk, err := boring.NewPublicKeyECDH(c.name, key) - if err != nil { - return nil, errors.New("crypto/ecdh: invalid public key") - } -- k.boring = bk -- } else { -- fk, err := c.newPublicKey(key) -- if err != nil { -- return nil, err -- } -- k.fips = fk -+ return &PublicKey{ -+ curve: c, -+ publicKey: bk.Bytes(), -+ boring: bk, -+ }, nil - } -- return k, nil -+ fk, err := c.newPublicKey(key) -+ if err != nil { -+ return nil, err -+ } -+ return &PublicKey{ -+ curve: c, -+ publicKey: bytes.Clone(key), -+ fips: fk, -+ }, nil - } - - func (c *nistCurve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) { -diff --git a/src/crypto/ecdh/x25519.go b/src/crypto/ecdh/x25519.go -index 21a921aa12d44d..be8b1441c62844 100644 ---- a/src/crypto/ecdh/x25519.go -+++ b/src/crypto/ecdh/x25519.go -@@ -6,6 +6,7 @@ package ecdh - - import ( - "bytes" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/edwards25519/field" - "crypto/internal/fips140only" - "crypto/internal/rand" -@@ -38,6 +39,23 @@ func (c *x25519Curve) GenerateKey(r io.Reader) (*PrivateKey, error) { - if fips140only.Enforced() { - return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode") - } -+ if boring.Enabled && boring.SupportsCurve("X25519") && rand.IsDefaultReader(r) { -+ key, bytes, err := boring.GenerateKeyECDH(c.String()) -+ if err != nil { -+ return nil, err -+ } -+ pub, err := key.PublicKey() -+ if err != nil { -+ return nil, err -+ } -+ k := &PrivateKey{ -+ curve: c, -+ privateKey: bytes, -+ publicKey: &PublicKey{curve: c, publicKey: pub.Bytes(), boring: pub}, -+ boring: key, -+ } -+ return k, nil -+ } - r = rand.CustomReader(r) - key := make([]byte, x25519PrivateKeySize) - if _, err := io.ReadFull(r, key); err != nil { -@@ -53,6 +71,23 @@ func (c *x25519Curve) NewPrivateKey(key []byte) (*PrivateKey, error) { - if len(key) != x25519PrivateKeySize { - return nil, errors.New("crypto/ecdh: invalid private key size") - } -+ if boring.Enabled && boring.SupportsCurve("X25519") { -+ bk, err := boring.NewPrivateKeyECDH(c.String(), key) -+ if err != nil { -+ return nil, errors.New("crypto/ecdh: invalid private key") -+ } -+ pub, err := bk.PublicKey() -+ if err != nil { -+ return nil, errors.New("crypto/ecdh: invalid private key") -+ } -+ k := &PrivateKey{ -+ curve: c, -+ privateKey: bytes.Clone(key), -+ publicKey: &PublicKey{curve: c, publicKey: pub.Bytes(), boring: pub}, -+ boring: bk, -+ } -+ return k, nil -+ } - publicKey := make([]byte, x25519PublicKeySize) - x25519Basepoint := [32]byte{9} - x25519ScalarMult(publicKey, key, x25519Basepoint[:]) -@@ -73,6 +108,17 @@ func (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) { - if len(key) != x25519PublicKeySize { - return nil, errors.New("crypto/ecdh: invalid public key") - } -+ if boring.Enabled && boring.SupportsCurve("X25519") { -+ bk, err := boring.NewPublicKeyECDH(c.String(), key) -+ if err != nil { -+ return nil, errors.New("crypto/ecdh: invalid public key") -+ } -+ return &PublicKey{ -+ curve: c, -+ publicKey: bk.Bytes(), -+ boring: bk, -+ }, nil -+ } - return &PublicKey{ - curve: c, - publicKey: bytes.Clone(key), -@@ -80,6 +126,9 @@ func (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) { - } - - func (c *x25519Curve) ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) { -+ if boring.Enabled && boring.SupportsCurve("X25519") { -+ return boring.ECDH(local.boring, remote.boring) -+ } - out := make([]byte, x25519SharedSecretSize) - x25519ScalarMult(out, local.privateKey, remote.publicKey) - if isZero(out) { -diff --git a/src/crypto/ecdsa/badlinkname.go b/src/crypto/ecdsa/badlinkname.go -new file mode 100644 -index 00000000000000..168efdb820c85b ---- /dev/null -+++ b/src/crypto/ecdsa/badlinkname.go -@@ -0,0 +1,19 @@ -+// Copyright 2025 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package ecdsa -+ -+import _ "unsafe" -+ -+// The symbols below are pulled by some crypto backend in the Microsoft build of Go compiler. -+// We provide a push linkname here, to keep them accessible with pull linknames. -+ -+//go:linkname encodeSignature -+//go:linkname parseSignature -+ -+// This supplements other linknames that are already added by -+// https://github.com/golang/go/commit/41aab30bd260297ad8ddad47e98fdf8390a9a67e -+// See that commit for more information. -diff --git a/src/crypto/ecdsa/boring.go b/src/crypto/ecdsa/boring.go -index 275c60b4de49eb..ff8bddf28c4545 100644 ---- a/src/crypto/ecdsa/boring.go -+++ b/src/crypto/ecdsa/boring.go -@@ -2,13 +2,13 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - package ecdsa - - import ( -- "crypto/internal/boring" -- "crypto/internal/boring/bbig" -+ boring "crypto/internal/backend" -+ "crypto/internal/backend/bbig" - "crypto/internal/boring/bcache" - "math/big" - ) -diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go -index 40a89017570171..a0fb823b7c9197 100644 ---- a/src/crypto/ecdsa/ecdsa.go -+++ b/src/crypto/ecdsa/ecdsa.go -@@ -20,8 +20,8 @@ import ( - "crypto" - "crypto/ecdh" - "crypto/elliptic" -- "crypto/internal/boring" -- "crypto/internal/boring/bbig" -+ boring "crypto/internal/backend" -+ "crypto/internal/backend/bbig" - "crypto/internal/fips140/ecdsa" - "crypto/internal/fips140/nistec" - "crypto/internal/fips140cache" -@@ -339,7 +339,7 @@ func (priv *PrivateKey) Sign(random io.Reader, digest []byte, opts crypto.Signer - // ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed - // in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom]. - func GenerateKey(c elliptic.Curve, r io.Reader) (*PrivateKey, error) { -- if boring.Enabled && rand.IsDefaultReader(r) { -+ if boring.Enabled && rand.IsDefaultReader(r) && boring.SupportsCurve(c.Params().Name) { - x, y, d, err := boring.GenerateKeyECDSA(c.Params().Name) - if err != nil { - return nil, err -@@ -389,7 +389,7 @@ func SignASN1(r io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) { - return nil, errors.New("ecdsa: hash cannot be empty") - } - -- if boring.Enabled && rand.IsDefaultReader(r) { -+ if boring.Enabled && rand.IsDefaultReader(r) && boring.SupportsCurve(priv.Curve.Params().Name) { - b, err := boringPrivateKey(priv) - if err != nil { - return nil, err -@@ -510,7 +510,7 @@ func VerifyASN1(pub *PublicKey, hash, sig []byte) bool { - return false - } - -- if boring.Enabled { -+ if boring.Enabled && boring.SupportsCurve(pub.Curve.Params().Name) { - key, err := boringPublicKey(pub) - if err != nil { - return false -diff --git a/src/crypto/ecdsa/notboring.go b/src/crypto/ecdsa/notboring.go -index 039bd82ed21f9f..69a97d9bf250be 100644 ---- a/src/crypto/ecdsa/notboring.go -+++ b/src/crypto/ecdsa/notboring.go -@@ -2,11 +2,11 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build !boringcrypto -+//go:build !goexperiment.systemcrypto - - package ecdsa - --import "crypto/internal/boring" -+import boring "crypto/internal/backend" - - func boringPublicKey(*PublicKey) (*boring.PublicKeyECDSA, error) { - panic("boringcrypto: not available") -diff --git a/src/crypto/ed25519/boring.go b/src/crypto/ed25519/boring.go -new file mode 100644 -index 00000000000000..4e18cdfbaad4fd ---- /dev/null -+++ b/src/crypto/ed25519/boring.go -@@ -0,0 +1,71 @@ -+// Copyright 2023 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package ed25519 -+ -+import ( -+ boring "crypto/internal/backend" -+ "crypto/internal/boring/bcache" -+ "unsafe" -+) -+ -+var pubCache bcache.Cache[byte, boringPub] -+var privCache bcache.Cache[byte, boringPriv] -+ -+func init() { -+ if boring.Enabled && boring.SupportsEd25519() { -+ pubCache.Register() -+ privCache.Register() -+ } -+} -+ -+type boringPub struct { -+ key boring.PublicKeyEd25519 -+ orig [PublicKeySize]byte -+} -+ -+func boringPublicKey(pub PublicKey) (boring.PublicKeyEd25519, error) { -+ // Use the pointer to the underlying pub array as key. -+ p := unsafe.SliceData(pub) -+ b := pubCache.Get(p) -+ if b != nil && PublicKey(b.orig[:]).Equal(pub) { -+ return b.key, nil -+ } -+ -+ b = new(boringPub) -+ copy(b.orig[:], pub) -+ key, err := boring.NewPublicKeyEd25519(b.orig[:]) -+ if err != nil { -+ return key, err -+ } -+ b.key = key -+ pubCache.Put(p, b) -+ return key, nil -+} -+ -+type boringPriv struct { -+ key boring.PrivateKeyEd25519 -+ orig [PrivateKeySize]byte -+} -+ -+func boringPrivateKey(priv PrivateKey) (boring.PrivateKeyEd25519, error) { -+ // Use the pointer to the underlying priv array as key. -+ p := unsafe.SliceData(priv) -+ b := privCache.Get(p) -+ if b != nil && PrivateKey(b.orig[:]).Equal(priv) { -+ return b.key, nil -+ } -+ -+ b = new(boringPriv) -+ copy(b.orig[:], priv) -+ key, err := boring.NewPrivateKeyEd25519(b.orig[:]) -+ if err != nil { -+ return key, err -+ } -+ b.key = key -+ privCache.Put(p, b) -+ return key, nil -+} -diff --git a/src/crypto/ed25519/ed25519.go b/src/crypto/ed25519/ed25519.go -index ed599ad2908dfb..b3106b9ed83811 100644 ---- a/src/crypto/ed25519/ed25519.go -+++ b/src/crypto/ed25519/ed25519.go -@@ -17,6 +17,7 @@ package ed25519 - - import ( - "crypto" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/ed25519" - "crypto/internal/fips140cache" - "crypto/internal/fips140only" -@@ -27,6 +28,7 @@ import ( - "internal/godebug" - "io" - "strconv" -+ "sync" - ) - - const ( -@@ -159,6 +161,19 @@ func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error) { - random = rand.Reader - } - } -+ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsEd25519() { -+ priv, err := boring.GenerateKeyEd25519() -+ if err != nil { -+ return nil, nil, err -+ } -+ privData, err := priv.Bytes() -+ if err != nil { -+ return nil, nil, err -+ } -+ privKey := PrivateKey(privData) -+ pubKey := privKey.Public().(PublicKey) -+ return pubKey, privKey, err -+ } - - if fips140only.Enforced() && !fips140only.ApprovedRandomReader(random) { - return nil, nil, errors.New("crypto/ed25519: only crypto/rand.Reader is allowed in FIPS 140-only mode") -@@ -188,6 +203,17 @@ func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error) { - // with RFC 8032. RFC 8032's private keys correspond to seeds in this - // package. - func NewKeyFromSeed(seed []byte) PrivateKey { -+ if boring.Enabled && boring.SupportsEd25519() { -+ key, err := boring.NewPrivateKeyEd25519FromSeed(seed) -+ if err != nil { -+ panic(err) -+ } -+ d, err := key.Bytes() -+ if err != nil { -+ panic(err) -+ } -+ return PrivateKey(d) -+ } - // Outline the function body so that the returned key can be stack-allocated. - privateKey := make([]byte, PrivateKeySize) - newKeyFromSeed(privateKey, seed) -@@ -206,6 +232,17 @@ func newKeyFromSeed(privateKey, seed []byte) { - // Sign signs the message with privateKey and returns a signature. It will - // panic if len(privateKey) is not [PrivateKeySize]. - func Sign(privateKey PrivateKey, message []byte) []byte { -+ if boring.Enabled && boring.SupportsEd25519() { -+ priv, err := boringPrivateKey(privateKey) -+ if err != nil { -+ panic(err) -+ } -+ signature, err := boring.SignEd25519(priv, message) -+ if err != nil { -+ panic(err) -+ } -+ return signature -+ } - // Outline the function body so that the returned signature can be - // stack-allocated. - signature := make([]byte, SignatureSize) -@@ -235,6 +272,32 @@ func Verify(publicKey PublicKey, message, sig []byte) bool { - return VerifyWithOptions(publicKey, message, sig, &Options{Hash: crypto.Hash(0)}) == nil - } - -+// testMalleability returns true if the crypto backend correctly rejects -+// malleable signatures. The only known backend that fails to do so is -+// OpenSSL version 1.1.1a or lower. -+// See https://github.com/openssl/openssl/issues/7693. -+var testMalleability = sync.OnceValue(func() bool { -+ msg := []byte{0x54, 0x65, 0x73, 0x74} -+ sig := []byte{ -+ 0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a, -+ 0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b, -+ 0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67, -+ 0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d, -+ 0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33, -+ 0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d, -+ } -+ pkey := []byte{ -+ 0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5, -+ 0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34, -+ 0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa, -+ } -+ pub, err := boring.NewPublicKeyEd25519(pkey) -+ if err != nil { -+ return false -+ } -+ return boring.VerifyEd25519(pub, msg, sig) != nil -+}) -+ - // VerifyWithOptions reports whether sig is a valid signature of message by - // publicKey. A valid signature is indicated by returning a nil error. It will - // panic if len(publicKey) is not [PublicKeySize]. -@@ -263,6 +326,13 @@ func VerifyWithOptions(publicKey PublicKey, message, sig []byte, opts *Options) - } - return ed25519.VerifyCtx(k, message, sig, opts.Context) - case opts.Hash == crypto.Hash(0): // Ed25519 -+ if boring.Enabled && boring.SupportsEd25519() && testMalleability() { -+ pub, err := boringPublicKey(publicKey) -+ if err != nil { -+ return err -+ } -+ return boring.VerifyEd25519(pub, message, sig) -+ } - return ed25519.Verify(k, message, sig) - default: - return errors.New("ed25519: expected opts.Hash zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)") -diff --git a/src/crypto/ed25519/ed25519_test.go b/src/crypto/ed25519/ed25519_test.go -index c8a23e3246a949..02dc683fa1a1b4 100644 ---- a/src/crypto/ed25519/ed25519_test.go -+++ b/src/crypto/ed25519/ed25519_test.go -@@ -9,12 +9,15 @@ import ( - "bytes" - "compress/gzip" - "crypto" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "crypto/rand" - "crypto/sha512" - "encoding/hex" -+ "internal/goexperiment" - "log" - "os" -+ "runtime" - "strings" - "testing" - ) -@@ -316,7 +319,7 @@ func TestGolden(t *testing.T) { - copy(priv[32:], pubKey) - - sig2 := Sign(priv[:], msg) -- if !bytes.Equal(sig, sig2[:]) { -+ if !bytes.Equal(sig, sig2[:]) && !(goexperiment.SystemCrypto && runtime.GOOS == "darwin") { - t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2) - } - -@@ -371,6 +374,11 @@ func TestAllocations(t *testing.T) { - cryptotest.SkipTestAllocations(t) - seed := make([]byte, SeedSize) - priv := NewKeyFromSeed(seed) -+ expectedAllocations := 0.0 -+ if boring.Enabled { -+ expectedAllocations = 6 -+ } -+ - if allocs := testing.AllocsPerRun(100, func() { - message := []byte("Hello, world!") - pub := priv.Public().(PublicKey) -@@ -378,8 +386,8 @@ func TestAllocations(t *testing.T) { - if !Verify(pub, message, signature) { - t.Fatal("signature didn't verify") - } -- }); allocs > 0 { -- t.Errorf("expected zero allocations, got %0.1f", allocs) -+ }); allocs > expectedAllocations { -+ t.Errorf("expected %v allocations, got %0.1f", expectedAllocations, allocs) - } - } - -diff --git a/src/crypto/ed25519/notboring.go b/src/crypto/ed25519/notboring.go -new file mode 100644 -index 00000000000000..77b69a3be88183 ---- /dev/null -+++ b/src/crypto/ed25519/notboring.go -@@ -0,0 +1,16 @@ -+// Copyright 2023 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !goexperiment.systemcrypto -+ -+package ed25519 -+ -+import boring "crypto/internal/backend" -+ -+func boringPublicKey(PublicKey) (boring.PublicKeyEd25519, error) { -+ panic("boringcrypto: not available") -+} -+func boringPrivateKey(PrivateKey) (boring.PrivateKeyEd25519, error) { -+ panic("boringcrypto: not available") -+} -diff --git a/src/crypto/fips140/enforcement_test.go b/src/crypto/fips140/enforcement_test.go -index d230c0f4ec1fa1..56020ea5f6c2a3 100644 ---- a/src/crypto/fips140/enforcement_test.go -+++ b/src/crypto/fips140/enforcement_test.go -@@ -7,6 +7,7 @@ package fips140_test - import ( - "crypto/des" - "crypto/fips140" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "testing" - ) -@@ -25,6 +26,9 @@ func isAllowed() bool { - } - - func TestWithoutEnforcement(t *testing.T) { -+ if boring.Enabled { -+ t.Skip("GODEBUG=fips140=only not supported") -+ } - if !fips140.Enforced() { - cryptotest.RerunWithFIPS140Enforced(t) - return -diff --git a/src/crypto/fips140/fips140.go b/src/crypto/fips140/fips140.go -index d3f63d3bf18fcb..7197af537228aa 100644 ---- a/src/crypto/fips140/fips140.go -+++ b/src/crypto/fips140/fips140.go -@@ -11,6 +11,7 @@ - package fips140 - - import ( -+ bfips140 "crypto/internal/backend/fips140" - "crypto/internal/fips140" - "crypto/internal/fips140/check" - ) -@@ -27,7 +28,7 @@ func Enabled() bool { - if fips140.Enabled && !check.Verified { - panic("crypto/fips140: FIPS 140-3 mode enabled, but integrity check didn't pass") - } -- return fips140.Enabled -+ return fips140.Enabled || bfips140.Enabled() - } - - // Version returns the FIPS 140-3 Go Cryptographic Module version (such as -diff --git a/src/crypto/hkdf/hkdf.go b/src/crypto/hkdf/hkdf.go -index 88439922a5032e..5a3ae17e80efbc 100644 ---- a/src/crypto/hkdf/hkdf.go -+++ b/src/crypto/hkdf/hkdf.go -@@ -11,6 +11,7 @@ - package hkdf - - import ( -+ boring "crypto/internal/backend" - "crypto/internal/fips140/hkdf" - "crypto/internal/fips140hash" - "crypto/internal/fips140only" -@@ -29,6 +30,9 @@ func Extract[H hash.Hash](h func() H, secret, salt []byte) ([]byte, error) { - if err := checkFIPS140Only(fh, secret); err != nil { - return nil, err - } -+ if boring.Enabled && boring.SupportsHKDF() { -+ return boring.ExtractHKDF(fh, secret, salt) -+ } - return hkdf.Extract(fh, secret, salt), nil - } - -@@ -50,6 +54,9 @@ func Expand[H hash.Hash](h func() H, pseudorandomKey []byte, info string, keyLen - return nil, errors.New("hkdf: requested key length too large") - } - -+ if boring.Enabled && boring.SupportsHKDF() { -+ return boring.ExpandHKDF(fh, pseudorandomKey, []byte(info), keyLength) -+ } - return hkdf.Expand(fh, pseudorandomKey, info, keyLength), nil - } - -@@ -67,6 +74,13 @@ func Key[Hash hash.Hash](h func() Hash, secret, salt []byte, info string, keyLen - return nil, errors.New("hkdf: requested key length too large") - } - -+ if boring.Enabled && boring.SupportsHKDF() { -+ pseudorandomKey, err := boring.ExtractHKDF(fh, secret, salt) -+ if err != nil { -+ return nil, err -+ } -+ return boring.ExpandHKDF(fh, pseudorandomKey, []byte(info), keyLength) -+ } - return hkdf.Key(fh, secret, salt, info, keyLength), nil - } - -diff --git a/src/crypto/hkdf/hkdf_test.go b/src/crypto/hkdf/hkdf_test.go -index 57d90f88e93e75..4069ab057a2525 100644 ---- a/src/crypto/hkdf/hkdf_test.go -+++ b/src/crypto/hkdf/hkdf_test.go -@@ -6,7 +6,7 @@ package hkdf - - import ( - "bytes" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140" - "crypto/md5" - "crypto/sha1" -diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go -index e7976e25193dfe..58b357ee52c328 100644 ---- a/src/crypto/hmac/hmac.go -+++ b/src/crypto/hmac/hmac.go -@@ -22,7 +22,7 @@ timing side-channels: - package hmac - - import ( -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/hmac" - "crypto/internal/fips140hash" - "crypto/internal/fips140only" -@@ -37,13 +37,6 @@ import ( - // the returned Hash does not implement [encoding.BinaryMarshaler] - // or [encoding.BinaryUnmarshaler]. - func New(h func() hash.Hash, key []byte) hash.Hash { -- if boring.Enabled { -- hm := boring.NewHMAC(h, key) -- if hm != nil { -- return hm -- } -- // BoringCrypto did not recognize h, so fall through to standard Go code. -- } - h = fips140hash.UnwrapNew(h) - if fips140only.Enforced() { - if len(key) < 112/8 { -@@ -53,6 +46,13 @@ func New(h func() hash.Hash, key []byte) hash.Hash { - panic("crypto/hmac: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - } -+ if boring.Enabled { -+ hm := boring.NewHMAC(h, key) -+ if hm != nil { -+ return hm -+ } -+ // BoringCrypto did not recognize h, so fall through to standard Go code. -+ } - return hmac.New(h, key) - } - -diff --git a/src/crypto/hmac/hmac_test.go b/src/crypto/hmac/hmac_test.go -index b324d1f02cda7d..1df17bdc90a1f0 100644 ---- a/src/crypto/hmac/hmac_test.go -+++ b/src/crypto/hmac/hmac_test.go -@@ -5,7 +5,7 @@ - package hmac - - import ( -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "crypto/internal/fips140hash" - "crypto/md5" -diff --git a/src/crypto/hpke/aead.go b/src/crypto/hpke/aead.go -index fb55c97ddf20c9..fb036863cde2dd 100644 ---- a/src/crypto/hpke/aead.go -+++ b/src/crypto/hpke/aead.go -@@ -6,6 +6,7 @@ package hpke - - import ( - "crypto/cipher" -+ boring "crypto/internal/backend" - "errors" - "fmt" - -@@ -77,10 +78,15 @@ var aes256GCM = &aead{ - } - - var chacha20poly1305AEAD = &aead{ -- nK: chacha20poly1305.KeySize, -- nN: chacha20poly1305.NonceSize, -- new: chacha20poly1305.New, -- id: 0x0003, -+ nK: chacha20poly1305.KeySize, -+ nN: chacha20poly1305.NonceSize, -+ new: func(key []byte) (cipher.AEAD, error) { -+ if boring.Enabled && boring.SupportsChaCha20Poly1305() { -+ return boring.NewChaCha20Poly1305(key) -+ } -+ return chacha20poly1305.New(key) -+ }, -+ id: 0x0003, - } - - func (a *aead) ID() uint16 { -diff --git a/src/crypto/internal/cryptotest/allocations.go b/src/crypto/internal/cryptotest/allocations.go -index 70055af70b42ec..21528faa0d6145 100644 ---- a/src/crypto/internal/cryptotest/allocations.go -+++ b/src/crypto/internal/cryptotest/allocations.go -@@ -5,7 +5,6 @@ - package cryptotest - - import ( -- "crypto/internal/boring" - "internal/asan" - "internal/msan" - "internal/race" -@@ -17,11 +16,6 @@ import ( - // SkipTestAllocations skips the test if there are any factors that interfere - // with allocation optimizations. - func SkipTestAllocations(t *testing.T) { -- // Go+BoringCrypto uses cgo. -- if boring.Enabled { -- t.Skip("skipping allocations test with BoringCrypto") -- } -- - // The sanitizers sometimes cause allocations. - if race.Enabled || msan.Enabled || asan.Enabled { - t.Skip("skipping allocations test with sanitizers") -diff --git a/src/crypto/internal/cryptotest/fips140.go b/src/crypto/internal/cryptotest/fips140.go -index a4c9fc977bc136..ef9fa5d540bd8b 100644 ---- a/src/crypto/internal/cryptotest/fips140.go -+++ b/src/crypto/internal/cryptotest/fips140.go -@@ -5,6 +5,8 @@ - package cryptotest - - import ( -+ boring "crypto/internal/backend" -+ bfips140 "crypto/internal/backend/fips140" - "crypto/internal/fips140" - "internal/testenv" - "regexp" -@@ -15,9 +17,15 @@ import ( - - func MustSupportFIPS140(tb testing.TB) { - tb.Helper() -+ if !boring.Enabled { -+ tb.Skip("skipping: boring not enabled") -+ } - if err := fips140.Supported(); err != nil { - tb.Skipf("test requires FIPS 140 mode: %v", err) - } -+ if !bfips140.Enabled() { -+ tb.Skipf("skipping: FIPS is not enabled") -+ } - } - - // MustMinimumFIPS140ModuleVersion skips the test if compiled against a lower -diff --git a/src/crypto/internal/cryptotest/hash.go b/src/crypto/internal/cryptotest/hash.go -index 37fd96a2d9d0b9..6d4b190831d57d 100644 ---- a/src/crypto/internal/cryptotest/hash.go -+++ b/src/crypto/internal/cryptotest/hash.go -@@ -5,7 +5,6 @@ - package cryptotest - - import ( -- "crypto/internal/boring" - "crypto/internal/fips140" - "hash" - "internal/testhash" -@@ -20,7 +19,7 @@ type MakeHash func() hash.Hash - // TestHash performs a set of tests on hash.Hash implementations, checking the - // documented requirements of Write, Sum, Reset, Size, and BlockSize. - func TestHash(t *testing.T, mh MakeHash) { -- if boring.Enabled || fips140.Version() == "v1.0.0" { -+ if fips140.Version() == "v1.0.0" { - testhash.TestHashWithoutClone(t, testhash.MakeHash(mh)) - return - } -diff --git a/src/crypto/internal/cryptotest/implementations.go b/src/crypto/internal/cryptotest/implementations.go -index 2b6cf4b75fc6b7..3a1b9e5cc67ecc 100644 ---- a/src/crypto/internal/cryptotest/implementations.go -+++ b/src/crypto/internal/cryptotest/implementations.go -@@ -5,7 +5,7 @@ - package cryptotest - - import ( -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/impl" - "internal/goarch" - "internal/goos" -diff --git a/src/crypto/internal/fips140hash/hash.go b/src/crypto/internal/fips140hash/hash.go -index 6d67ee8b3429a1..8f8d5937ea913c 100644 ---- a/src/crypto/internal/fips140hash/hash.go -+++ b/src/crypto/internal/fips140hash/hash.go -@@ -5,14 +5,13 @@ - package fips140hash - - import ( -- fsha3 "crypto/internal/fips140/sha3" - "crypto/sha3" - "hash" - _ "unsafe" - ) - - //go:linkname sha3Unwrap --func sha3Unwrap(*sha3.SHA3) *fsha3.Digest -+func sha3Unwrap(*sha3.SHA3) hash.Hash - - // Unwrap returns h, or a crypto/internal/fips140 inner implementation of h. - // -diff --git a/src/crypto/internal/fips140only/fips140only_test.go b/src/crypto/internal/fips140only/fips140only_test.go -index a940fb2a3a0010..878d1cb8115abb 100644 ---- a/src/crypto/internal/fips140only/fips140only_test.go -+++ b/src/crypto/internal/fips140only/fips140only_test.go -@@ -17,6 +17,7 @@ import ( - "crypto/hkdf" - "crypto/hmac" - "crypto/hpke" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "crypto/internal/fips140" - "crypto/internal/fips140only" -@@ -38,6 +39,7 @@ import ( - "io" - "math/big" - "os" -+ "runtime" - "strings" - "testing" - -@@ -81,7 +83,12 @@ func testFIPS140Only(t *testing.T) { - expectPanic(t, func() { cipher.NewCFBEncrypter(aesBlock, iv) }) - expectPanic(t, func() { cipher.NewCFBDecrypter(aesBlock, iv) }) - -- cipher.NewCTR(aesBlock, iv) -+ if boring.Enabled && (runtime.GOOS == "darwin" || runtime.GOOS == "windows") { -+ // The darwin/windows crypto backends don't support CTR. -+ expectPanic(t, func() { cipher.NewCTR(aesBlock, iv) }) -+ } else { -+ cipher.NewCTR(aesBlock, iv) -+ } - expectPanic(t, func() { cipher.NewCTR(notAESBlock, iv) }) - - expectPanic(t, func() { cipher.NewOFB(aesBlock, iv) }) -@@ -306,10 +313,12 @@ bXVL8iKLrG91IYQByUHZIn3WVAd2bfi4MfKagRt0ggd4 - expectErr(t, errRet2(hpke.Seal(hpkeK.PublicKey(), hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), nil, nil))) - expectErr(t, errRet2(hpke.Open(hpkeK, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), nil, make([]byte, 2000)))) - -- // fips140=only mode should prevent any operation that would make the FIPS -- // 140-3 module set its service indicator to false. -- if !fips140.ServiceIndicator() { -- t.Errorf("service indicator not set") -+ if !boring.Enabled { -+ // fips140=only mode should prevent any operation that would make the FIPS -+ // 140-3 module set its service indicator to false. -+ if !fips140.ServiceIndicator() { -+ t.Errorf("service indicator not set") -+ } - } - } - -@@ -322,16 +331,22 @@ type readerWrap struct { - } - - func withApprovedHash(f func(crypto.Hash)) { -- f(crypto.SHA224) -- f(crypto.SHA256) -- f(crypto.SHA384) -- f(crypto.SHA512) -- f(crypto.SHA3_224) -- f(crypto.SHA3_256) -- f(crypto.SHA3_384) -- f(crypto.SHA3_512) -- f(crypto.SHA512_224) -- f(crypto.SHA512_256) -+ for _, h := range []crypto.Hash{ -+ crypto.SHA224, crypto.SHA256, crypto.SHA384, crypto.SHA512, -+ crypto.SHA512_224, crypto.SHA512_256, -+ } { -+ if boring.Enabled && !boring.SupportsHash(h) { -+ continue -+ } -+ f(h) -+ } -+ for _, h := range []crypto.Hash{crypto.SHA3_224, crypto.SHA3_256, crypto.SHA3_384, crypto.SHA3_512} { -+ if boring.Enabled { -+ // TODO implement fallbacks that checks hash.Hash -+ continue -+ } -+ f(h) -+ } - } - - func withNonApprovedHash(f func(crypto.Hash)) { -diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go -index 90e152e1484d5d..b0d4dfc77c6225 100644 ---- a/src/crypto/internal/fips140test/acvp_test.go -+++ b/src/crypto/internal/fips140test/acvp_test.go -@@ -22,6 +22,8 @@ import ( - "bufio" - "bytes" - "crypto/elliptic" -+ boring "crypto/internal/backend" -+ bfips140 "crypto/internal/backend/fips140" - "crypto/internal/cryptotest" - entropy "crypto/internal/entropy/v1.0.0" - "crypto/internal/fips140" -@@ -2105,6 +2107,10 @@ func cmdKtsIfcResponderAft(h func() hash.Hash) command { - func TestACVP(t *testing.T) { - testenv.SkipIfShortAndSlow(t) - -+ if boring.Enabled && !bfips140.Enabled() { -+ t.Skipf("skipping: FIPS is not enabled") -+ } -+ - const ( - bsslModule = "boringssl.googlesource.com/boringssl.git" - bsslVersion = "v0.0.0-20251111011041-baaf868e6e8f" -diff --git a/src/crypto/internal/fips140test/cast_test.go b/src/crypto/internal/fips140test/cast_test.go -index 817dcb9a35a793..8efc547d0d52fc 100644 ---- a/src/crypto/internal/fips140test/cast_test.go -+++ b/src/crypto/internal/fips140test/cast_test.go -@@ -160,6 +160,7 @@ func TestConditionals(t *testing.T) { - } - - func TestCASTPasses(t *testing.T) { -+ t.Skip("Skipping because Microsoft build of Go doesn't support CAST.") - moduleStatus(t) - cryptotest.MustSupportFIPS140(t) - -@@ -183,6 +184,7 @@ func TestCASTPasses(t *testing.T) { - } - - func TestCASTFailures(t *testing.T) { -+ t.Skip("Skipping because Microsoft build of Go doesn't support CAST.") - moduleStatus(t) - cryptotest.MustSupportFIPS140(t) - -diff --git a/src/crypto/internal/fips140test/fips_test.go b/src/crypto/internal/fips140test/fips_test.go -index 7f2824ca9ac052..f0d3b2a8459871 100644 ---- a/src/crypto/internal/fips140test/fips_test.go -+++ b/src/crypto/internal/fips140test/fips_test.go -@@ -15,7 +15,7 @@ package fipstest - - import ( - "bytes" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140" - "crypto/internal/fips140/aes" - "crypto/internal/fips140/aes/gcm" -diff --git a/src/crypto/internal/rand/rand.go b/src/crypto/internal/rand/rand.go -index 5845cfe92bbffb..26f23eac5855dc 100644 ---- a/src/crypto/internal/rand/rand.go -+++ b/src/crypto/internal/rand/rand.go -@@ -5,7 +5,7 @@ - package rand - - import ( -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/drbg" - "crypto/internal/randutil" - "internal/godebug" -diff --git a/src/crypto/md5/md5.go b/src/crypto/md5/md5.go -index f1287887ff5e25..61e9cc23e5667f 100644 ---- a/src/crypto/md5/md5.go -+++ b/src/crypto/md5/md5.go -@@ -12,6 +12,7 @@ package md5 - - import ( - "crypto" -+ boring "crypto/internal/backend" - "crypto/internal/fips140only" - "errors" - "hash" -@@ -114,6 +115,9 @@ func (d *digest) Clone() (hash.Cloner, error) { - // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal - // state of the hash. - func New() hash.Hash { -+ if boring.Enabled && boring.SupportsHash(crypto.MD5) && !fips140only.Enforced() { -+ return boring.NewMD5() -+ } - d := new(digest) - d.Reset() - return d -@@ -203,6 +207,12 @@ func (d *digest) checkSum() [Size]byte { - - // Sum returns the MD5 checksum of the data. - func Sum(data []byte) [Size]byte { -+ if boring.Enabled && boring.SupportsHash(crypto.MD5) { -+ if fips140only.Enforced() { -+ panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") -+ } -+ return boring.MD5(data) -+ } - var d digest - d.Reset() - d.Write(data) -diff --git a/src/crypto/md5/md5_test.go b/src/crypto/md5/md5_test.go -index 403ff2881f4b68..5417556a86700e 100644 ---- a/src/crypto/md5/md5_test.go -+++ b/src/crypto/md5/md5_test.go -@@ -6,9 +6,11 @@ package md5 - - import ( - "bytes" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "crypto/rand" - "encoding" -+ "errors" - "fmt" - "hash" - "io" -@@ -96,6 +98,9 @@ func TestGoldenMarshal(t *testing.T) { - - state, err := h.(encoding.BinaryMarshaler).MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("could not marshal: %v", err) - continue - } -@@ -183,6 +188,9 @@ func TestExtraLarge(t *testing.T) { - - // Tests that blockGeneric (pure Go) and block (in assembly for amd64, 386, arm) match. - func TestBlockGeneric(t *testing.T) { -+ if boring.Enabled { -+ t.Skip("digest is not used when boring.Enabled is set") -+ } - gen, asm := New().(*digest), New().(*digest) - buf := make([]byte, BlockSize*20) // arbitrary factor - rand.Read(buf) -@@ -230,10 +238,18 @@ func safeSum(h hash.Hash) (sum []byte, err error) { - } - - func TestLargeHashes(t *testing.T) { -+ if boring.Enabled { -+ if _, ok := New().(encoding.BinaryUnmarshaler); !ok { -+ t.Skip("BinaryUnmarshaler not implemented") -+ } -+ } - for i, test := range largeUnmarshalTests { - - h := New() - if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("test %d could not unmarshal: %v", i, err) - continue - } -@@ -271,7 +287,7 @@ func TestMD5Hash(t *testing.T) { - - func TestExtraMethods(t *testing.T) { - h := maybeCloner(New()) -- cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - } - - func maybeCloner(h hash.Hash) any { -diff --git a/src/crypto/mlkem/mlkem.go b/src/crypto/mlkem/mlkem.go -index f0fd6a4993a8e6..53600fdf265062 100644 ---- a/src/crypto/mlkem/mlkem.go -+++ b/src/crypto/mlkem/mlkem.go -@@ -13,7 +13,9 @@ package mlkem - - import ( - "crypto" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/mlkem" -+ "crypto/internal/rand" - ) - - const ( -@@ -36,38 +38,66 @@ const ( - EncapsulationKeySize1024 = 1568 - ) - --// DecapsulationKey768 is the secret key used to decapsulate a shared key -+// A DecapsulationKey768 is the secret key used to decapsulate a shared key - // from a ciphertext. It includes various precomputed values. - type DecapsulationKey768 struct { -- key *mlkem.DecapsulationKey768 -+ key *mlkem.DecapsulationKey768 -+ boringKey boring.DecapsulationKeyMLKEM768 -+} -+ -+func supportsBoringMLKEM768() bool { -+ return boring.Enabled && rand.IsDefaultReader(rand.Reader) && boring.SupportsMLKEM768() -+} -+ -+func supportsBoringMLKEM1024() bool { -+ return boring.Enabled && rand.IsDefaultReader(rand.Reader) && boring.SupportsMLKEM1024() - } - - // GenerateKey768 generates a new decapsulation key, drawing random bytes from - // a secure source. The decapsulation key must be kept secret. - func GenerateKey768() (*DecapsulationKey768, error) { -+ if supportsBoringMLKEM768() { -+ key, err := boring.GenerateKeyMLKEM768() -+ if err != nil { -+ return nil, err -+ } -+ return &DecapsulationKey768{boringKey: key}, nil -+ } -+ - key, err := mlkem.GenerateKey768() - if err != nil { - return nil, err - } - -- return &DecapsulationKey768{key}, nil -+ return &DecapsulationKey768{key: key}, nil - } - - // NewDecapsulationKey768 expands a decapsulation key from a 64-byte seed in the - // "d || z" form. The seed must be uniformly random. - func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) { -+ if supportsBoringMLKEM768() { -+ key, err := boring.NewDecapsulationKeyMLKEM768(seed) -+ if err != nil { -+ return nil, err -+ } -+ return &DecapsulationKey768{boringKey: key}, nil -+ } -+ - key, err := mlkem.NewDecapsulationKey768(seed) - if err != nil { - return nil, err - } - -- return &DecapsulationKey768{key}, nil -+ return &DecapsulationKey768{key: key}, nil - } - - // Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. - // - // The decapsulation key must be kept secret. - func (dk *DecapsulationKey768) Bytes() []byte { -+ if dk.key == nil { -+ return dk.boringKey.Bytes() -+ } - return dk.key.Bytes() - } - -@@ -79,13 +109,19 @@ func (dk *DecapsulationKey768) Bytes() []byte { - // - // The shared key must be kept secret. - func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { -+ if dk.key == nil { -+ return dk.boringKey.Decapsulate(ciphertext) -+ } - return dk.key.Decapsulate(ciphertext) - } - - // EncapsulationKey returns the public encapsulation key necessary to produce - // ciphertexts. - func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { -- return &EncapsulationKey768{dk.key.EncapsulationKey()} -+ if dk.key == nil { -+ return &EncapsulationKey768{boringKey: dk.boringKey.EncapsulationKey()} -+ } -+ return &EncapsulationKey768{key: dk.key.EncapsulationKey()} - } - - // Encapsulator returns the encapsulation key, like -@@ -101,22 +137,34 @@ var _ crypto.Decapsulator = (*DecapsulationKey768)(nil) - // An EncapsulationKey768 is the public key used to produce ciphertexts to be - // decapsulated by the corresponding DecapsulationKey768. - type EncapsulationKey768 struct { -- key *mlkem.EncapsulationKey768 -+ key *mlkem.EncapsulationKey768 -+ boringKey boring.EncapsulationKeyMLKEM768 - } - - // NewEncapsulationKey768 parses an encapsulation key from its encoded form. If - // the encapsulation key is not valid, NewEncapsulationKey768 returns an error. - func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) { -+ if supportsBoringMLKEM768() { -+ key, err := boring.NewEncapsulationKeyMLKEM768(encapsulationKey) -+ if err != nil { -+ return nil, err -+ } -+ return &EncapsulationKey768{boringKey: key}, nil -+ } -+ - key, err := mlkem.NewEncapsulationKey768(encapsulationKey) - if err != nil { - return nil, err - } - -- return &EncapsulationKey768{key}, nil -+ return &EncapsulationKey768{key: key}, nil - } - - // Bytes returns the encapsulation key as a byte slice. - func (ek *EncapsulationKey768) Bytes() []byte { -+ if ek.key == nil { -+ return ek.boringKey.Bytes() -+ } - return ek.key.Bytes() - } - -@@ -128,41 +176,64 @@ func (ek *EncapsulationKey768) Bytes() []byte { - // For testing, derandomized encapsulation is provided by the - // [crypto/mlkem/mlkemtest] package. - func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) { -+ if ek.key == nil { -+ return ek.boringKey.Encapsulate() -+ } - return ek.key.Encapsulate() - } - - // DecapsulationKey1024 is the secret key used to decapsulate a shared key - // from a ciphertext. It includes various precomputed values. - type DecapsulationKey1024 struct { -- key *mlkem.DecapsulationKey1024 -+ key *mlkem.DecapsulationKey1024 -+ boringKey boring.DecapsulationKeyMLKEM1024 - } - - // GenerateKey1024 generates a new decapsulation key, drawing random bytes from - // a secure source. The decapsulation key must be kept secret. - func GenerateKey1024() (*DecapsulationKey1024, error) { -+ if supportsBoringMLKEM1024() { -+ key, err := boring.GenerateKeyMLKEM1024() -+ if err != nil { -+ return nil, err -+ } -+ return &DecapsulationKey1024{boringKey: key}, nil -+ } -+ - key, err := mlkem.GenerateKey1024() - if err != nil { - return nil, err - } - -- return &DecapsulationKey1024{key}, nil -+ return &DecapsulationKey1024{key: key}, nil - } - - // NewDecapsulationKey1024 expands a decapsulation key from a 64-byte seed in the - // "d || z" form. The seed must be uniformly random. - func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) { -+ if supportsBoringMLKEM1024() { -+ key, err := boring.NewDecapsulationKeyMLKEM1024(seed) -+ if err != nil { -+ return nil, err -+ } -+ return &DecapsulationKey1024{boringKey: key}, nil -+ } -+ - key, err := mlkem.NewDecapsulationKey1024(seed) - if err != nil { - return nil, err - } - -- return &DecapsulationKey1024{key}, nil -+ return &DecapsulationKey1024{key: key}, nil - } - - // Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. - // - // The decapsulation key must be kept secret. - func (dk *DecapsulationKey1024) Bytes() []byte { -+ if dk.key == nil { -+ return dk.boringKey.Bytes() -+ } - return dk.key.Bytes() - } - -@@ -174,13 +245,19 @@ func (dk *DecapsulationKey1024) Bytes() []byte { - // - // The shared key must be kept secret. - func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { -+ if dk.key == nil { -+ return dk.boringKey.Decapsulate(ciphertext) -+ } - return dk.key.Decapsulate(ciphertext) - } - - // EncapsulationKey returns the public encapsulation key necessary to produce - // ciphertexts. - func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { -- return &EncapsulationKey1024{dk.key.EncapsulationKey()} -+ if dk.key == nil { -+ return &EncapsulationKey1024{boringKey: dk.boringKey.EncapsulationKey()} -+ } -+ return &EncapsulationKey1024{key: dk.key.EncapsulationKey()} - } - - // Encapsulator returns the encapsulation key, like -@@ -196,22 +273,34 @@ var _ crypto.Decapsulator = (*DecapsulationKey1024)(nil) - // An EncapsulationKey1024 is the public key used to produce ciphertexts to be - // decapsulated by the corresponding DecapsulationKey1024. - type EncapsulationKey1024 struct { -- key *mlkem.EncapsulationKey1024 -+ key *mlkem.EncapsulationKey1024 -+ boringKey boring.EncapsulationKeyMLKEM1024 - } - - // NewEncapsulationKey1024 parses an encapsulation key from its encoded form. If - // the encapsulation key is not valid, NewEncapsulationKey1024 returns an error. - func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) { -+ if supportsBoringMLKEM1024() { -+ key, err := boring.NewEncapsulationKeyMLKEM1024(encapsulationKey) -+ if err != nil { -+ return nil, err -+ } -+ return &EncapsulationKey1024{boringKey: key}, nil -+ } -+ - key, err := mlkem.NewEncapsulationKey1024(encapsulationKey) - if err != nil { - return nil, err - } - -- return &EncapsulationKey1024{key}, nil -+ return &EncapsulationKey1024{key: key}, nil - } - - // Bytes returns the encapsulation key as a byte slice. - func (ek *EncapsulationKey1024) Bytes() []byte { -+ if ek.key == nil { -+ return ek.boringKey.Bytes() -+ } - return ek.key.Bytes() - } - -@@ -223,5 +312,8 @@ func (ek *EncapsulationKey1024) Bytes() []byte { - // For testing, derandomized encapsulation is provided by the - // [crypto/mlkem/mlkemtest] package. - func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) { -+ if ek.key == nil { -+ return ek.boringKey.Encapsulate() -+ } - return ek.key.Encapsulate() - } -diff --git a/src/crypto/mlkem/mlkem_test.go b/src/crypto/mlkem/mlkem_test.go -index e1c2ef49f15ce0..23504c64f4d805 100644 ---- a/src/crypto/mlkem/mlkem_test.go -+++ b/src/crypto/mlkem/mlkem_test.go -@@ -6,6 +6,7 @@ package mlkem_test - - import ( - "bytes" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/mlkem" - "crypto/internal/fips140/sha3" - . "crypto/mlkem" -@@ -164,6 +165,10 @@ var millionFlag = flag.Bool("million", false, "run the million vector test") - // TestAccumulated accumulates 10k (or 100, or 1M) random vectors and checks the - // hash of the result, to avoid checking in 150MB of test vectors. - func TestAccumulated(t *testing.T) { -+ if boring.Enabled { -+ t.Skip("skipping TestAccumulated with backend enabled (EncapsulateInternal not available)") -+ } -+ - n := 10000 - expected := "8a518cc63da366322a8e7a818c7a0d63483cb3528d34a4cf42f35d5ad73f22fc" - if testing.Short() { -@@ -234,6 +239,9 @@ func BenchmarkKeyGen(b *testing.B) { - } - - func BenchmarkEncaps(b *testing.B) { -+ if boring.Enabled { -+ b.Skip("skipping BenchmarkEncaps with backend enabled (EncapsulateInternal not available)") -+ } - seed := make([]byte, SeedSize) - rand.Read(seed) - dk, err := NewDecapsulationKey768(seed) -diff --git a/src/crypto/pbkdf2/pbkdf2.go b/src/crypto/pbkdf2/pbkdf2.go -index 0bc14be888d9d6..4172402e1bb620 100644 ---- a/src/crypto/pbkdf2/pbkdf2.go -+++ b/src/crypto/pbkdf2/pbkdf2.go -@@ -11,6 +11,7 @@ - package pbkdf2 - - import ( -+ boring "crypto/internal/backend" - "crypto/internal/fips140/pbkdf2" - "crypto/internal/fips140hash" - "crypto/internal/fips140only" -@@ -50,5 +51,11 @@ func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyL - return nil, errors.New("crypto/pbkdf2: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - } -+ if boring.Enabled && boring.SupportsPBKDF2() { -+ if keyLength <= 0 { -+ return nil, errors.New("pkbdf2: keyLength must be larger than 0") -+ } -+ return boring.PBKDF2([]byte(password), salt, iter, keyLength, fh) -+ } - return pbkdf2.Key(fh, password, salt, iter, keyLength) - } -diff --git a/src/crypto/pbkdf2/pbkdf2_test.go b/src/crypto/pbkdf2/pbkdf2_test.go -index eb0ed14e243c6b..cd7223c89eeae5 100644 ---- a/src/crypto/pbkdf2/pbkdf2_test.go -+++ b/src/crypto/pbkdf2/pbkdf2_test.go -@@ -6,12 +6,13 @@ package pbkdf2_test - - import ( - "bytes" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140" - "crypto/pbkdf2" - "crypto/sha1" - "crypto/sha256" - "hash" -+ "internal/goexperiment" - "testing" - ) - -@@ -223,6 +224,9 @@ func TestPBKDF2ServiceIndicator(t *testing.T) { - } - - func TestMaxKeyLength(t *testing.T) { -+ if goexperiment.SystemCrypto { -+ t.Skip("Some backends don't support keys longer than 32 bytes") -+ } - // This error cannot be triggered on platforms where int is 31 bits (i.e. - // 32-bit platforms), since the max value for keyLength is 1<<31-1 and - // 1<<31-1 * hLen will always be less than 1<<32-1 * hLen. -diff --git a/src/crypto/purego_test.go b/src/crypto/purego_test.go -index ebc9110cecee82..aa4aec046795cf 100644 ---- a/src/crypto/purego_test.go -+++ b/src/crypto/purego_test.go -@@ -41,7 +41,7 @@ func TestPureGoTag(t *testing.T) { - } - - for _, pkgName := range pkgs { -- if strings.Contains(pkgName, "/boring") { -+ if strings.Contains(pkgName, "/boring") || strings.Contains(pkgName, "/internal/backend") || strings.Contains(pkgName, "tls/fipsonly") { - continue - } - -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go -index 018fe013cef600..ec710c6f935e3b 100644 ---- a/src/crypto/rand/rand.go -+++ b/src/crypto/rand/rand.go -@@ -7,7 +7,7 @@ - package rand - - import ( -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/drbg" - "crypto/internal/rand" - "io" -diff --git a/src/crypto/rand/rand_test.go b/src/crypto/rand/rand_test.go -index 13e4a65db28bd2..3404dd39d73c6f 100644 ---- a/src/crypto/rand/rand_test.go -+++ b/src/crypto/rand/rand_test.go -@@ -7,6 +7,7 @@ package rand - import ( - "bytes" - "compress/flate" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "crypto/internal/rand" - "errors" -@@ -156,13 +157,17 @@ var sink byte - - func TestAllocations(t *testing.T) { - cryptotest.SkipTestAllocations(t) -+ expectedAllocations := 0 -+ if boring.Enabled { -+ expectedAllocations = 1 -+ } - n := int(testing.AllocsPerRun(10, func() { - buf := make([]byte, 32) - Read(buf) - sink ^= buf[0] - })) -- if n > 0 { -- t.Errorf("allocs = %d, want 0", n) -+ if n > expectedAllocations { -+ t.Errorf("allocs = %d, want %d", n, expectedAllocations) - } - } - -diff --git a/src/crypto/rc4/rc4.go b/src/crypto/rc4/rc4.go -index c4d2b0d382e7fc..ba8dc141117e59 100644 ---- a/src/crypto/rc4/rc4.go -+++ b/src/crypto/rc4/rc4.go -@@ -10,6 +10,7 @@ - package rc4 - - import ( -+ boring "crypto/internal/backend" - "crypto/internal/fips140/alias" - "crypto/internal/fips140only" - "errors" -@@ -20,6 +21,8 @@ import ( - type Cipher struct { - s [256]uint32 - i, j uint8 -+ -+ boring *boring.RC4Cipher - } - - type KeySizeError int -@@ -38,6 +41,13 @@ func NewCipher(key []byte) (*Cipher, error) { - if k < 1 || k > 256 { - return nil, KeySizeError(k) - } -+ if boring.Enabled && boring.SupportsRC4() { -+ c, err := boring.NewRC4Cipher(key) -+ if err != nil { -+ return nil, err -+ } -+ return &Cipher{boring: c}, nil -+ } - var c Cipher - for i := 0; i < 256; i++ { - c.s[i] = uint32(i) -@@ -55,6 +65,10 @@ func NewCipher(key []byte) (*Cipher, error) { - // Deprecated: Reset can't guarantee that the key will be entirely removed from - // the process's memory. - func (c *Cipher) Reset() { -+ if boring.Enabled && boring.SupportsRC4() { -+ c.boring.Reset() -+ return -+ } - clear(c.s[:]) - c.i, c.j = 0, 0 - } -@@ -62,6 +76,10 @@ func (c *Cipher) Reset() { - // XORKeyStream sets dst to the result of XORing src with the key stream. - // Dst and src must overlap entirely or not at all. - func (c *Cipher) XORKeyStream(dst, src []byte) { -+ if boring.Enabled && boring.SupportsRC4() { -+ c.boring.XORKeyStream(dst, src) -+ return -+ } - if len(src) == 0 { - return - } -diff --git a/src/crypto/rsa/boring.go b/src/crypto/rsa/boring.go -index b9f9d3154f2589..27bdf13d2268ce 100644 ---- a/src/crypto/rsa/boring.go -+++ b/src/crypto/rsa/boring.go -@@ -2,15 +2,17 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - package rsa - - import ( -- "crypto/internal/boring" -- "crypto/internal/boring/bbig" -+ boring "crypto/internal/backend" -+ "crypto/internal/backend/bbig" - "crypto/internal/boring/bcache" -+ "internal/goexperiment" - "math/big" -+ "runtime" - ) - - // Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. -@@ -62,6 +64,11 @@ type boringPriv struct { - } - - func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyRSA, error) { -+ // CommonCrypto requires the CRT values to be precomputed if nil -+ if goexperiment.SystemCrypto && runtime.GOOS == "darwin" && (priv.Precomputed.Dp == nil || priv.Precomputed.Dq == nil || priv.Precomputed.Qinv == nil) { -+ priv.Precompute() -+ priv.Precomputed.fips = nil -+ } - b := privCache.Get(priv) - if b != nil && privateKeyEqual(&b.orig, priv) { - return b.key, nil -diff --git a/src/crypto/rsa/boring_test.go b/src/crypto/rsa/boring_test.go -index 838fcc1244bdbe..d89f732345e8a3 100644 ---- a/src/crypto/rsa/boring_test.go -+++ b/src/crypto/rsa/boring_test.go -@@ -2,7 +2,7 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - // Note: Can run these tests against the non-BoringCrypto - // version of the code by using "CGO_ENABLED=0 go test". -diff --git a/src/crypto/rsa/fips.go b/src/crypto/rsa/fips.go -index e756512fecd6d5..9b35eab6e3a811 100644 ---- a/src/crypto/rsa/fips.go -+++ b/src/crypto/rsa/fips.go -@@ -6,7 +6,7 @@ package rsa - - import ( - "crypto" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/rsa" - "crypto/internal/fips140hash" - "crypto/internal/fips140only" -@@ -71,17 +71,6 @@ func SignPSS(random io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte - hash = opts.Hash - } - -- if boring.Enabled && rand.IsDefaultReader(random) && priv.N.BitLen() >= 1024 { -- bkey, err := boringPrivateKey(priv) -- if err != nil { -- return nil, err -- } -- return boring.SignRSAPSS(bkey, hash, digest, opts.saltLength()) -- } -- if priv.N.BitLen() >= 1024 { -- boring.UnreachableExceptTests() -- } -- - if !hash.Available() { - return nil, errors.New("crypto/rsa: requested hash function unavailable: " + hash.String()) - } -@@ -97,6 +86,14 @@ func SignPSS(random io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte - return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") - } - -+ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSASaltLength(true, opts.saltLength()) && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsHash(hash) { -+ bkey, err := boringPrivateKey(priv) -+ if err != nil { -+ return nil, err -+ } -+ return boring.SignRSAPSS(bkey, hash, digest, opts.saltLength()) -+ } -+ - k, err := fipsPrivateKey(priv) - if err != nil { - return nil, err -@@ -139,17 +136,6 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts - return err - } - -- if boring.Enabled { -- bkey, err := boringPublicKey(pub) -- if err != nil { -- return err -- } -- if err := boring.VerifyRSAPSS(bkey, hash, digest, sig, opts.saltLength()); err != nil { -- return ErrVerification -- } -- return nil -- } -- - if !hash.Available() { - return errors.New("crypto/rsa: requested hash function unavailable: " + hash.String()) - } -@@ -162,6 +148,17 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts - return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - -+ if boring.Enabled && boring.SupportsRSASaltLength(false, opts.saltLength()) && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsHash(hash) { -+ bkey, err := boringPublicKey(pub) -+ if err != nil { -+ return err -+ } -+ if err := boring.VerifyRSAPSS(bkey, hash, digest, sig, opts.saltLength()); err != nil { -+ return ErrVerification -+ } -+ return nil -+ } -+ - k, err := fipsPublicKey(pub) - if err != nil { - return err -@@ -217,10 +214,12 @@ func EncryptOAEPWithOptions(random io.Reader, pub *PublicKey, msg []byte, opts * - if opts.MGFHash != 0 && !opts.MGFHash.Available() { - return nil, errors.New("crypto/rsa: requested hash function unavailable: " + opts.MGFHash.String()) - } -- if opts.MGFHash == 0 { -- return encryptOAEP(opts.Hash.New(), opts.Hash.New(), random, pub, msg, opts.Label) -+ hash := opts.Hash.New() -+ mgfhash := hash -+ if opts.MGFHash != 0 && opts.Hash != opts.MGFHash { -+ mgfhash = opts.MGFHash.New() - } -- return encryptOAEP(opts.Hash.New(), opts.MGFHash.New(), random, pub, msg, opts.Label) -+ return encryptOAEP(hash, mgfhash, random, pub, msg, opts.Label) - } - - func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) { -@@ -231,7 +230,7 @@ func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *Publi - defer hash.Reset() - defer mgfHash.Reset() - -- if boring.Enabled && rand.IsDefaultReader(random) { -+ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAOAEPLabel(label) && hash == mgfHash { - k := pub.Size() - if len(msg) > k-2*hash.Size()-2 { - return nil, ErrMessageTooLong -@@ -242,7 +241,6 @@ func encryptOAEP(hash hash.Hash, mgfHash hash.Hash, random io.Reader, pub *Publi - } - return boring.EncryptRSAOAEP(hash, mgfHash, bkey, msg, label) - } -- boring.UnreachableExceptTests() - - hash = fips140hash.Unwrap(hash) - -@@ -283,7 +281,7 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l - return nil, err - } - -- if boring.Enabled && priv.N.BitLen() >= 1024 { -+ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAOAEPLabel(label) && hash == mgfHash { - k := priv.Size() - if len(ciphertext) > k || - k < hash.Size()*2+2 { -@@ -345,23 +343,23 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ - return nil, err - } - -- if boring.Enabled && priv.N.BitLen() >= 1024 { -- bkey, err := boringPrivateKey(priv) -- if err != nil { -- return nil, err -- } -- return boring.SignRSAPKCS1v15(bkey, hash, hashed) -- } -- - if err := checkFIPS140OnlyPrivateKey(priv); err != nil { - return nil, err - } - if fips140only.Enforced() { -- if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { -+ if hash == crypto.Hash(0) || !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - } - -+ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAPKCS1v15Signature(hash) { -+ bkey, err := boringPrivateKey(priv) -+ if err != nil { -+ return nil, err -+ } -+ return boring.SignRSAPKCS1v15(bkey, hash, hashed) -+ } -+ - k, err := fipsPrivateKey(priv) - if err != nil { - return nil, err -@@ -390,7 +388,16 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) - return err - } - -- if boring.Enabled { -+ if err := checkFIPS140OnlyPublicKey(pub); err != nil { -+ return err -+ } -+ if fips140only.Enforced() { -+ if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { -+ return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") -+ } -+ } -+ -+ if boring.Enabled && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAPKCS1v15Signature(hash) { - bkey, err := boringPublicKey(pub) - if err != nil { - return err -@@ -401,15 +408,6 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) - return nil - } - -- if err := checkFIPS140OnlyPublicKey(pub); err != nil { -- return err -- } -- if fips140only.Enforced() { -- if !hash.Available() || !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { -- return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") -- } -- } -- - k, err := fipsPublicKey(pub) - if err != nil { - return err -diff --git a/src/crypto/rsa/notboring.go b/src/crypto/rsa/notboring.go -index 2abc0436405f8a..3e4d6f3eef61e6 100644 ---- a/src/crypto/rsa/notboring.go -+++ b/src/crypto/rsa/notboring.go -@@ -2,11 +2,11 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build !boringcrypto -+//go:build !goexperiment.systemcrypto - - package rsa - --import "crypto/internal/boring" -+import boring "crypto/internal/backend" - - func boringPublicKey(*PublicKey) (*boring.PublicKeyRSA, error) { - panic("boringcrypto: not available") -diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go -index bfe7346fc67c54..003030ec762ebf 100644 ---- a/src/crypto/rsa/pkcs1v15.go -+++ b/src/crypto/rsa/pkcs1v15.go -@@ -5,7 +5,7 @@ - package rsa - - import ( -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/rsa" - "crypto/internal/fips140only" - "crypto/internal/rand" -@@ -61,7 +61,7 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro - return nil, ErrMessageTooLong - } - -- if boring.Enabled && rand.IsDefaultReader(random) { -+ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAPKCS1v15Encryption() { - bkey, err := boringPublicKey(pub) - if err != nil { - return nil, err -@@ -83,7 +83,7 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro - em[len(em)-len(msg)-1] = 0 - copy(mm, msg) - -- if boring.Enabled { -+ if boring.Enabled && boring.SupportsRSAPublicKey(pub.N.BitLen()) && boring.SupportsRSAPKCS1v15Encryption() { - var bkey *boring.PublicKeyRSA - bkey, err = boringPublicKey(pub) - if err != nil { -@@ -115,7 +115,7 @@ func DecryptPKCS1v15(random io.Reader, priv *PrivateKey, ciphertext []byte) ([]b - return nil, err - } - -- if boring.Enabled && priv.N.BitLen() >= 1024 { -+ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAPKCS1v15Encryption() { - bkey, err := boringPrivateKey(priv) - if err != nil { - return nil, err -@@ -221,7 +221,7 @@ func decryptPKCS1v15(priv *PrivateKey, ciphertext []byte) (valid int, em []byte, - return 0, nil, 0, err - } - -- if boring.Enabled && priv.N.BitLen() >= 1024 { -+ if boring.Enabled && boring.SupportsRSAPrivateKey(priv.N.BitLen(), len(priv.Primes)) && boring.SupportsRSAPKCS1v15Encryption() { - var bkey *boring.PrivateKeyRSA - bkey, err = boringPrivateKey(priv) - if err != nil { -diff --git a/src/crypto/rsa/pkcs1v15_test.go b/src/crypto/rsa/pkcs1v15_test.go -index c65552cd93526a..910416abe842f5 100644 ---- a/src/crypto/rsa/pkcs1v15_test.go -+++ b/src/crypto/rsa/pkcs1v15_test.go -@@ -7,6 +7,7 @@ package rsa_test - import ( - "bytes" - "crypto" -+ boring "crypto/internal/backend" - "crypto/rand" - . "crypto/rsa" - "crypto/sha1" -@@ -235,6 +236,10 @@ func TestVerifyPKCS1v15(t *testing.T) { - } - - func TestOverlongMessagePKCS1v15(t *testing.T) { -+ if boring.Enabled { -+ // See for example https://github.com/golang-fips/openssl/pull/142. -+ t.Skip("Some backends do not return an error for overlong messages.") -+ } - t.Setenv("GODEBUG", "rsa1024min=0") - ciphertext := decodeBase64("fjOVdirUzFoLlukv80dBllMLjXythIf22feqPrNo0YoIjzyzyoMFiLjAc/Y4krkeZ11XFThIrEvw\nkRiZcCq5ng==") - _, err := DecryptPKCS1v15(nil, test512Key, ciphertext) -diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go -index e03f4ab06603c6..a9d9daba2fd6ce 100644 ---- a/src/crypto/rsa/pss_test.go -+++ b/src/crypto/rsa/pss_test.go -@@ -8,14 +8,17 @@ import ( - "bufio" - "compress/bzip2" - "crypto" -+ boring "crypto/internal/backend" - "crypto/internal/fips140" - "crypto/rand" - . "crypto/rsa" - "crypto/sha256" - "crypto/sha512" - "encoding/hex" -+ "internal/goexperiment" - "math/big" - "os" -+ "runtime" - "strconv" - "strings" - "testing" -@@ -104,6 +107,9 @@ func TestPSSGolden(t *testing.T) { - hashed = h.Sum(hashed[:0]) - - if err := VerifyPSS(key, hash, hashed, sig, opts); err != nil { -+ if goexperiment.SystemCrypto && runtime.GOOS == "darwin" && key.N.BitLen() == 1025 { -+ t.Skip("CommonCrypto doesn't support golden test entries with this key size") -+ } - t.Error(err) - } - default: -@@ -180,6 +186,10 @@ func TestPSSSigning(t *testing.T) { - continue - } - -+ if boring.Enabled && test.good != test.fipsGood { -+ t.Skip("skipping test with different results when using different backends") -+ } -+ - opts.SaltLength = test.verifySaltLength - err = VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, &opts) - good := test.good -@@ -241,7 +251,9 @@ func fromHex(hexStr string) []byte { - - func TestInvalidPSSSaltLength(t *testing.T) { - t.Setenv("GODEBUG", "rsa1024min=0") -- key, err := GenerateKey(rand.Reader, 245) -+ // Most crypto backends don't support generating RSA keys with a bit size -+ // lower than 512, so we use 512 here. -+ key, err := GenerateKey(rand.Reader, 512) - if err != nil { - t.Fatal(err) - } -diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go -index 93e08ed6cd8f71..2e8392c7ac34cf 100644 ---- a/src/crypto/rsa/rsa.go -+++ b/src/crypto/rsa/rsa.go -@@ -43,8 +43,8 @@ package rsa - - import ( - "crypto" -- "crypto/internal/boring" -- "crypto/internal/boring/bbig" -+ boring "crypto/internal/backend" -+ "crypto/internal/backend/bbig" - "crypto/internal/fips140/bigmod" - "crypto/internal/fips140/rsa" - "crypto/internal/fips140only" -@@ -182,11 +182,12 @@ func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.D - if opts.MGFHash != 0 && !opts.MGFHash.Available() { - return nil, errors.New("rsa: requested hash function unavailable: " + opts.MGFHash.String()) - } -- if opts.MGFHash == 0 { -- return decryptOAEP(opts.Hash.New(), opts.Hash.New(), priv, ciphertext, opts.Label) -- } else { -- return decryptOAEP(opts.Hash.New(), opts.MGFHash.New(), priv, ciphertext, opts.Label) -+ hash := opts.Hash.New() -+ mgfhash := hash -+ if opts.MGFHash != 0 && opts.Hash != opts.MGFHash { -+ mgfhash = opts.MGFHash.New() - } -+ return decryptOAEP(hash, mgfhash, priv, ciphertext, opts.Label) - - case *PKCS1v15DecryptOptions: - if l := opts.SessionKeyLen; l > 0 { -@@ -320,8 +321,14 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { - return nil, err - } - -- if boring.Enabled && rand.IsDefaultReader(random) && -- (bits == 2048 || bits == 3072 || bits == 4096) { -+ if fips140only.Enforced() && bits < 2048 { -+ return nil, errors.New("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode") -+ } -+ if fips140only.Enforced() && bits%2 == 1 { -+ return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode") -+ } -+ -+ if boring.Enabled && rand.IsDefaultReader(random) && boring.SupportsRSAPublicKey(bits) { - bN, bE, bD, bP, bQ, bDp, bDq, bQinv, err := boring.GenerateKeyRSA(bits) - if err != nil { - return nil, err -@@ -358,12 +365,6 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { - - random = rand.CustomReader(random) - -- if fips140only.Enforced() && bits < 2048 { -- return nil, errors.New("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode") -- } -- if fips140only.Enforced() && bits%2 == 1 { -- return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode") -- } - if fips140only.Enforced() && !fips140only.ApprovedRandomReader(random) { - return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") - } -diff --git a/src/crypto/rsa/rsa_darwin.go b/src/crypto/rsa/rsa_darwin.go -new file mode 100644 -index 00000000000000..b549c6e3b17484 ---- /dev/null -+++ b/src/crypto/rsa/rsa_darwin.go -@@ -0,0 +1,71 @@ -+// Copyright 2017 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package rsa -+ -+import ( -+ "crypto/internal/backend" -+ "crypto/internal/backend/bbig" -+ "errors" -+ "math/big" -+ _ "unsafe" -+ -+ "golang.org/x/crypto/cryptobyte" -+ "golang.org/x/crypto/cryptobyte/asn1" -+) -+ -+//go:linkname decodeKey -+func decodeKey(data []byte) (N, E, D, P, Q, Dp, Dq, Qinv backend.BigInt, err error) { -+ bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv backend.BigInt, err error) { -+ return nil, nil, nil, nil, nil, nil, nil, nil, e -+ } -+ input := cryptobyte.String(data) -+ var version int -+ n, e, d, p, q, dp, dq, qinv := new(big.Int), new(big.Int), new(big.Int), new(big.Int), -+ new(big.Int), new(big.Int), new(big.Int), new(big.Int) -+ // Parse the ASN.1 sequence -+ if !input.ReadASN1(&input, asn1.SEQUENCE) { -+ return bad(errors.New("invalid ASN.1 structure: not a sequence")) -+ } -+ if !input.ReadASN1Integer(&version) || version != 0 { -+ return bad(errors.New("invalid ASN.1 structure: unsupported version")) -+ } -+ if !input.ReadASN1Integer(n) || !input.ReadASN1Integer(e) || -+ !input.ReadASN1Integer(d) || !input.ReadASN1Integer(p) || -+ !input.ReadASN1Integer(q) || !input.ReadASN1Integer(dp) || -+ !input.ReadASN1Integer(dq) || !input.ReadASN1Integer(qinv) { -+ return bad(errors.New("invalid ASN.1 structure")) -+ } -+ return bbig.Enc(n), bbig.Enc(e), bbig.Enc(d), bbig.Enc(p), bbig.Enc(q), -+ bbig.Enc(dp), bbig.Enc(dq), bbig.Enc(qinv), nil -+} -+ -+//go:linkname encodeKey -+func encodeKey(N, E, D, P, Q, Dp, Dq, Qinv backend.BigInt) ([]byte, error) { -+ builder := cryptobyte.NewBuilder(nil) -+ builder.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { -+ b.AddASN1Int64(0) // Add version as int64 -+ b.AddASN1BigInt(bbig.Dec(N)) // Add modulus -+ b.AddASN1BigInt(bbig.Dec(E)) // Add public exponent -+ b.AddASN1BigInt(bbig.Dec(D)) // Add private exponent -+ b.AddASN1BigInt(bbig.Dec(P)) // Add prime1 -+ b.AddASN1BigInt(bbig.Dec(Q)) // Add prime2 -+ b.AddASN1BigInt(bbig.Dec(Dp)) // Add exponent1 -+ b.AddASN1BigInt(bbig.Dec(Dq)) // Add exponent2 -+ b.AddASN1BigInt(bbig.Dec(Qinv)) // Add coefficient -+ }) -+ return builder.Bytes() -+} -+ -+//go:linkname encodePublicKey -+func encodePublicKey(N, E backend.BigInt) ([]byte, error) { -+ builder := cryptobyte.NewBuilder(nil) -+ builder.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { -+ b.AddASN1BigInt(bbig.Dec(N)) // Add modulus -+ b.AddASN1BigInt(bbig.Dec(E)) // Add public exponent -+ }) -+ return builder.Bytes() -+} -diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go -index 8c1b22bf4f41b4..ca8a3740e8d31a 100644 ---- a/src/crypto/rsa/rsa_test.go -+++ b/src/crypto/rsa/rsa_test.go -@@ -8,7 +8,7 @@ import ( - "bufio" - "bytes" - "crypto" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "crypto/internal/fips140" - "crypto/internal/fips140/ecdsa" -@@ -228,6 +228,11 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) { - if priv.D.Cmp(priv.N) > 0 { - t.Errorf("private exponent too large") - } -+ if boring.Enabled && priv.N.BitLen() < 512 { -+ // Some crypto backends (e.g. CNG and OpenSSL with SymCrypt) don't support key sizes -+ // lower than 512 and intentionally fail rather than fall back to Go crypto. -+ t.Skip("skipping allocations test with BoringCrypto") -+ } - - msg := []byte("hi!") - enc, err := EncryptPKCS1v15(rand.Reader, &priv.PublicKey, msg) -diff --git a/src/crypto/sha1/sha1.go b/src/crypto/sha1/sha1.go -index 46e47df1d32cf2..05a1368f9833e5 100644 ---- a/src/crypto/sha1/sha1.go -+++ b/src/crypto/sha1/sha1.go -@@ -10,7 +10,7 @@ package sha1 - - import ( - "crypto" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140only" - "errors" - "hash" -@@ -113,7 +113,7 @@ func (d *digest) Reset() { - // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal - // state of the hash. - func New() hash.Hash { -- if boring.Enabled { -+ if boring.Enabled && !fips140only.Enforced() { - return boring.NewSHA1() - } - d := new(digest) -@@ -153,7 +153,6 @@ func (d *digest) Write(p []byte) (nn int, err error) { - } - - func (d *digest) Sum(in []byte) []byte { -- boring.Unreachable() - // Make a copy of d so that caller can keep writing and summing. - d0 := *d - hash := d0.checkSum() -@@ -271,12 +270,12 @@ func (d *digest) constSum() [Size]byte { - - // Sum returns the SHA-1 checksum of the data. - func Sum(data []byte) [Size]byte { -- if boring.Enabled { -- return boring.SHA1(data) -- } - if fips140only.Enforced() { - panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") - } -+ if boring.Enabled { -+ return boring.SHA1(data) -+ } - var d digest - d.Reset() - d.Write(data) -diff --git a/src/crypto/sha1/sha1_test.go b/src/crypto/sha1/sha1_test.go -index ef6e5ddcbb2d97..cc88965f966aae 100644 ---- a/src/crypto/sha1/sha1_test.go -+++ b/src/crypto/sha1/sha1_test.go -@@ -8,9 +8,10 @@ package sha1 - - import ( - "bytes" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "encoding" -+ "errors" - "fmt" - "hash" - "io" -@@ -112,6 +113,9 @@ func testGoldenMarshal(t *testing.T) { - - state, err := h.(encoding.BinaryMarshaler).MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("could not marshal: %v", err) - continue - } -@@ -204,6 +208,9 @@ func testLargeHashes(t *testing.T) { - for i, test := range largeUnmarshalTests { - h := New() - if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("test %d could not unmarshal: %v", i, err) - continue - } -@@ -244,7 +251,7 @@ func TestSHA1Hash(t *testing.T) { - func TestExtraMethods(t *testing.T) { - h := maybeCloner(New()) - cryptotest.NoExtraMethods(t, &h, "ConstantTimeSum", -- "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - } - - func maybeCloner(h hash.Hash) any { -diff --git a/src/crypto/sha256/sha256.go b/src/crypto/sha256/sha256.go -index 069938a22dbc5a..8d0e06b86f4359 100644 ---- a/src/crypto/sha256/sha256.go -+++ b/src/crypto/sha256/sha256.go -@@ -8,7 +8,7 @@ package sha256 - - import ( - "crypto" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/sha256" - "hash" - ) -@@ -43,7 +43,7 @@ func New() hash.Hash { - // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal - // state of the hash. - func New224() hash.Hash { -- if boring.Enabled { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA224) { - return boring.NewSHA224() - } - return sha256.New224() -@@ -63,7 +63,7 @@ func Sum256(data []byte) [Size]byte { - - // Sum224 returns the SHA224 checksum of the data. - func Sum224(data []byte) [Size224]byte { -- if boring.Enabled { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA224) { - return boring.SHA224(data) - } - h := New224() -diff --git a/src/crypto/sha256/sha256_test.go b/src/crypto/sha256/sha256_test.go -index a18a536ba2896f..7b3b37c3b0c10d 100644 ---- a/src/crypto/sha256/sha256_test.go -+++ b/src/crypto/sha256/sha256_test.go -@@ -8,10 +8,13 @@ package sha256 - - import ( - "bytes" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "encoding" -+ "errors" - "fmt" - "hash" -+ "internal/goexperiment" - "io" - "testing" - ) -@@ -163,6 +166,9 @@ func testGoldenMarshal(t *testing.T) { - - state, err := h.(encoding.BinaryMarshaler).MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("could not marshal: %v", err) - continue - } -@@ -256,6 +262,9 @@ func TestMarshalTypeMismatch(t *testing.T) { - - state1, err := h1.(encoding.BinaryMarshaler).MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("could not marshal: %v", err) - } - -@@ -325,10 +334,18 @@ func safeSum(h hash.Hash) (sum []byte, err error) { - return h.Sum(nil), nil - } - func TestLargeHashes(t *testing.T) { -+ if boring.Enabled { -+ if _, ok := New().(encoding.BinaryUnmarshaler); !ok { -+ t.Skip("BinaryUnmarshaler not implemented") -+ } -+ } - for i, test := range largeUnmarshalTests { - - h := New() - if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("test %d could not unmarshal: %v", i, err) - continue - } -@@ -347,6 +364,10 @@ func TestLargeHashes(t *testing.T) { - - func TestAllocations(t *testing.T) { - cryptotest.SkipTestAllocations(t) -+ expectedAllocations := 0.0 -+ if boring.Enabled { -+ expectedAllocations = 1 -+ } - if n := testing.AllocsPerRun(10, func() { - in := []byte("hello, world!") - out := make([]byte, 0, Size) -@@ -357,17 +378,10 @@ func TestAllocations(t *testing.T) { - h.Write(in) - out = h.Sum(out[:0]) - } -- { -- h := New224() -- h.Reset() -- h.Write(in) -- out = h.Sum(out[:0]) -- } - - Sum256(in) -- Sum224(in) -- }); n > 0 { -- t.Errorf("allocs = %v, want 0", n) -+ }); n > expectedAllocations { -+ t.Errorf("allocs = %v, want %v", n, expectedAllocations) - } - } - -@@ -404,13 +418,13 @@ func TestExtraMethods(t *testing.T) { - t.Run("SHA-224", func(t *testing.T) { - cryptotest.TestAllImplementations(t, "sha256", func(t *testing.T) { - h := maybeCloner(New224()) -- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - }) - }) - t.Run("SHA-256", func(t *testing.T) { - cryptotest.TestAllImplementations(t, "sha256", func(t *testing.T) { - h := maybeCloner(New()) -- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - }) - }) - } -@@ -481,7 +495,11 @@ func TestAllocatonsWithTypeAsserts(t *testing.T) { - marshaled, _ = h.(encoding.BinaryAppender).AppendBinary(marshaled[:0]) - h.(encoding.BinaryUnmarshaler).UnmarshalBinary(marshaled) - }) -- if allocs != 0 { -- t.Fatalf("allocs = %v; want = 0", allocs) -+ maxAllocs := 0 -+ if goexperiment.SystemCrypto { -+ maxAllocs = 2 -+ } -+ if allocs > float64(maxAllocs) { -+ t.Fatalf("allocs = %v; want <= %v", allocs, maxAllocs) - } - } -diff --git a/src/crypto/sha3/sha3.go b/src/crypto/sha3/sha3.go -index f7c5549ddd113c..71f915d4c05045 100644 ---- a/src/crypto/sha3/sha3.go -+++ b/src/crypto/sha3/sha3.go -@@ -8,6 +8,7 @@ package sha3 - - import ( - "crypto" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/sha3" - "hash" - _ "unsafe" -@@ -22,6 +23,9 @@ func init() { - - // Sum224 returns the SHA3-224 hash of data. - func Sum224(data []byte) [28]byte { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_224) { -+ return boring.SumSHA3_224(data) -+ } - var out [28]byte - h := sha3.New224() - h.Write(data) -@@ -31,6 +35,9 @@ func Sum224(data []byte) [28]byte { - - // Sum256 returns the SHA3-256 hash of data. - func Sum256(data []byte) [32]byte { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_256) { -+ return boring.SumSHA3_256(data) -+ } - var out [32]byte - h := sha3.New256() - h.Write(data) -@@ -40,6 +47,9 @@ func Sum256(data []byte) [32]byte { - - // Sum384 returns the SHA3-384 hash of data. - func Sum384(data []byte) [48]byte { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_384) { -+ return boring.SumSHA3_384(data) -+ } - var out [48]byte - h := sha3.New384() - h.Write(data) -@@ -49,6 +59,9 @@ func Sum384(data []byte) [48]byte { - - // Sum512 returns the SHA3-512 hash of data. - func Sum512(data []byte) [64]byte { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_512) { -+ return boring.SumSHA3_512(data) -+ } - var out [64]byte - h := sha3.New512() - h.Write(data) -@@ -59,6 +72,9 @@ func Sum512(data []byte) [64]byte { - // SumSHAKE128 applies the SHAKE128 extendable output function to data and - // returns an output of the given length in bytes. - func SumSHAKE128(data []byte, length int) []byte { -+ if boring.Enabled && boring.SupportsSHAKE(128) { -+ return boring.SumSHAKE128(data, length) -+ } - // Outline the allocation for up to 256 bits of output to the caller's stack. - out := make([]byte, 32) - return sumSHAKE128(out, data, length) -@@ -79,6 +95,9 @@ func sumSHAKE128(out, data []byte, length int) []byte { - // SumSHAKE256 applies the SHAKE256 extendable output function to data and - // returns an output of the given length in bytes. - func SumSHAKE256(data []byte, length int) []byte { -+ if boring.Enabled && boring.SupportsSHAKE(256) { -+ return boring.SumSHAKE256(data, length) -+ } - // Outline the allocation for up to 512 bits of output to the caller's stack. - out := make([]byte, 64) - return sumSHAKE256(out, data, length) -@@ -99,37 +118,54 @@ func sumSHAKE256(out, data []byte, length int) []byte { - // SHA3 is an instance of a SHA-3 hash. It implements [hash.Hash]. - // The zero value is a usable SHA3-256 hash. - type SHA3 struct { -- s sha3.Digest -+ s sha3.Digest -+ boringS *boring.Hash -+ supported bool - } - - //go:linkname fips140hash_sha3Unwrap crypto/internal/fips140hash.sha3Unwrap --func fips140hash_sha3Unwrap(s *SHA3) *sha3.Digest { -+func fips140hash_sha3Unwrap(s *SHA3) hash.Hash { - s.init() -+ if s.boringS != nil { -+ return s.boringS -+ } - return &s.s - } - - // New224 creates a new SHA3-224 hash. - func New224() *SHA3 { -- return &SHA3{*sha3.New224()} -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_224) { -+ return &SHA3{boringS: boring.NewSHA3_224(), supported: true} -+ } -+ return &SHA3{s: *sha3.New224()} - } - - // New256 creates a new SHA3-256 hash. - func New256() *SHA3 { -- return &SHA3{*sha3.New256()} -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_256) { -+ return &SHA3{boringS: boring.NewSHA3_256(), supported: true} -+ } -+ return &SHA3{s: *sha3.New256()} - } - - // New384 creates a new SHA3-384 hash. - func New384() *SHA3 { -- return &SHA3{*sha3.New384()} -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_384) { -+ return &SHA3{boringS: boring.NewSHA3_384(), supported: true} -+ } -+ return &SHA3{s: *sha3.New384()} - } - - // New512 creates a new SHA3-512 hash. - func New512() *SHA3 { -- return &SHA3{*sha3.New512()} -+ if boring.Enabled && boring.SupportsHash(crypto.SHA3_512) { -+ return &SHA3{boringS: boring.NewSHA3_512(), supported: true} -+ } -+ return &SHA3{s: *sha3.New512()} - } - - func (s *SHA3) init() { -- if s.s.Size() == 0 { -+ if s.s.Size() == 0 && s.boringS == nil { - *s = *New256() - } - } -@@ -137,53 +173,81 @@ func (s *SHA3) init() { - // Write absorbs more data into the hash's state. - func (s *SHA3) Write(p []byte) (n int, err error) { - s.init() -+ if boring.Enabled && s.supported { -+ return s.boringS.Write(p) -+ } - return s.s.Write(p) - } - - // Sum appends the current hash to b and returns the resulting slice. - func (s *SHA3) Sum(b []byte) []byte { - s.init() -+ if boring.Enabled && s.supported { -+ return s.boringS.Sum(b) -+ } - return s.s.Sum(b) - } - - // Reset resets the hash to its initial state. - func (s *SHA3) Reset() { - s.init() -- s.s.Reset() -+ if boring.Enabled && s.supported { -+ s.boringS.Reset() -+ } else { -+ s.s.Reset() -+ } - } - - // Size returns the number of bytes Sum will produce. - func (s *SHA3) Size() int { - s.init() -+ if boring.Enabled && s.supported { -+ return s.boringS.Size() -+ } - return s.s.Size() - } - - // BlockSize returns the hash's rate. - func (s *SHA3) BlockSize() int { - s.init() -+ if boring.Enabled && s.supported { -+ return s.boringS.BlockSize() -+ } - return s.s.BlockSize() - } - - // MarshalBinary implements [encoding.BinaryMarshaler]. - func (s *SHA3) MarshalBinary() ([]byte, error) { - s.init() -+ if boring.Enabled && s.supported { -+ return s.boringS.MarshalBinary() -+ } - return s.s.MarshalBinary() - } - - // AppendBinary implements [encoding.BinaryAppender]. - func (s *SHA3) AppendBinary(p []byte) ([]byte, error) { - s.init() -+ if boring.Enabled && s.supported { -+ return s.boringS.AppendBinary(p) -+ } - return s.s.AppendBinary(p) - } - - // UnmarshalBinary implements [encoding.BinaryUnmarshaler]. - func (s *SHA3) UnmarshalBinary(data []byte) error { - s.init() -+ if boring.Enabled && s.supported { -+ return s.boringS.UnmarshalBinary(data) -+ } - return s.s.UnmarshalBinary(data) - } - - // Clone implements [hash.Cloner]. - func (d *SHA3) Clone() (hash.Cloner, error) { -+ if boring.Enabled && d.supported { -+ return d.boringS.Clone() -+ } - r := *d - return &r, nil - } -@@ -191,23 +255,30 @@ func (d *SHA3) Clone() (hash.Cloner, error) { - // SHAKE is an instance of a SHAKE extendable output function. - // The zero value is a usable SHAKE256 hash. - type SHAKE struct { -- s sha3.SHAKE -+ s sha3.SHAKE -+ boringS *boring.SHAKE - } - - func (s *SHAKE) init() { -- if s.s.Size() == 0 { -+ if s.s.Size() == 0 && s.boringS == nil { - *s = *NewSHAKE256() - } - } - - // NewSHAKE128 creates a new SHAKE128 XOF. - func NewSHAKE128() *SHAKE { -- return &SHAKE{*sha3.NewShake128()} -+ if boring.Enabled && boring.SupportsSHAKE(128) { -+ return &SHAKE{boringS: boring.NewSHAKE128()} -+ } -+ return &SHAKE{s: *sha3.NewShake128()} - } - - // NewSHAKE256 creates a new SHAKE256 XOF. - func NewSHAKE256() *SHAKE { -- return &SHAKE{*sha3.NewShake256()} -+ if boring.Enabled && boring.SupportsSHAKE(256) { -+ return &SHAKE{boringS: boring.NewSHAKE256()} -+ } -+ return &SHAKE{s: *sha3.NewShake256()} - } - - // NewCSHAKE128 creates a new cSHAKE128 XOF. -@@ -216,7 +287,10 @@ func NewSHAKE256() *SHAKE { - // cSHAKE is desired. S is a customization byte string used for domain - // separation. When N and S are both empty, this is equivalent to NewSHAKE128. - func NewCSHAKE128(N, S []byte) *SHAKE { -- return &SHAKE{*sha3.NewCShake128(N, S)} -+ if boring.Enabled && boring.SupportsCSHAKE(128) { -+ return &SHAKE{boringS: boring.NewCSHAKE128(N, S)} -+ } -+ return &SHAKE{s: *sha3.NewCShake128(N, S)} - } - - // NewCSHAKE256 creates a new cSHAKE256 XOF. -@@ -225,7 +299,10 @@ func NewCSHAKE128(N, S []byte) *SHAKE { - // cSHAKE is desired. S is a customization byte string used for domain - // separation. When N and S are both empty, this is equivalent to NewSHAKE256. - func NewCSHAKE256(N, S []byte) *SHAKE { -- return &SHAKE{*sha3.NewCShake256(N, S)} -+ if boring.Enabled && boring.SupportsCSHAKE(256) { -+ return &SHAKE{boringS: boring.NewCSHAKE256(N, S)} -+ } -+ return &SHAKE{s: *sha3.NewCShake256(N, S)} - } - - // Write absorbs more data into the XOF's state. -@@ -233,6 +310,9 @@ func NewCSHAKE256(N, S []byte) *SHAKE { - // It panics if any output has already been read. - func (s *SHAKE) Write(p []byte) (n int, err error) { - s.init() -+ if boring.Enabled && s.boringS != nil { -+ return s.boringS.Write(p) -+ } - return s.s.Write(p) - } - -@@ -241,35 +321,54 @@ func (s *SHAKE) Write(p []byte) (n int, err error) { - // Any call to Write after a call to Read will panic. - func (s *SHAKE) Read(p []byte) (n int, err error) { - s.init() -+ if boring.Enabled && s.boringS != nil { -+ return s.boringS.Read(p) -+ } - return s.s.Read(p) - } - - // Reset resets the XOF to its initial state. - func (s *SHAKE) Reset() { - s.init() -- s.s.Reset() -+ if boring.Enabled && s.boringS != nil { -+ s.boringS.Reset() -+ } else { -+ s.s.Reset() -+ } - } - - // BlockSize returns the rate of the XOF. - func (s *SHAKE) BlockSize() int { - s.init() -+ if boring.Enabled && s.boringS != nil { -+ return s.boringS.BlockSize() -+ } - return s.s.BlockSize() - } - - // MarshalBinary implements [encoding.BinaryMarshaler]. - func (s *SHAKE) MarshalBinary() ([]byte, error) { - s.init() -+ if boring.Enabled && s.boringS != nil { -+ return s.boringS.MarshalBinary() -+ } - return s.s.MarshalBinary() - } - - // AppendBinary implements [encoding.BinaryAppender]. - func (s *SHAKE) AppendBinary(p []byte) ([]byte, error) { - s.init() -+ if boring.Enabled && s.boringS != nil { -+ return s.boringS.AppendBinary(p) -+ } - return s.s.AppendBinary(p) - } - - // UnmarshalBinary implements [encoding.BinaryUnmarshaler]. - func (s *SHAKE) UnmarshalBinary(data []byte) error { - s.init() -+ if boring.Enabled && s.boringS != nil { -+ return s.boringS.UnmarshalBinary(data) -+ } - return s.s.UnmarshalBinary(data) - } -diff --git a/src/crypto/sha3/sha3_test.go b/src/crypto/sha3/sha3_test.go -index 3973e0afd1b935..4a92e2652a50c4 100644 ---- a/src/crypto/sha3/sha3_test.go -+++ b/src/crypto/sha3/sha3_test.go -@@ -6,9 +6,11 @@ package sha3_test - - import ( - "bytes" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - . "crypto/sha3" - "encoding/hex" -+ "errors" - "hash" - "io" - "math/rand" -@@ -225,6 +227,10 @@ var sinkSHA3 byte - - func TestAllocations(t *testing.T) { - cryptotest.SkipTestAllocations(t) -+ expectedAllocations := 0.0 -+ if boring.Enabled { -+ expectedAllocations = 2 -+ } - t.Run("New", func(t *testing.T) { - if allocs := testing.AllocsPerRun(10, func() { - h := New256() -@@ -233,7 +239,7 @@ func TestAllocations(t *testing.T) { - out := make([]byte, 0, 32) - out = h.Sum(out) - sinkSHA3 ^= out[0] -- }); allocs > 0 { -+ }); allocs > expectedAllocations { - t.Errorf("expected zero allocations, got %0.1f", allocs) - } - }) -@@ -245,7 +251,7 @@ func TestAllocations(t *testing.T) { - out := make([]byte, 32) - h.Read(out) - sinkSHA3 ^= out[0] -- }); allocs > 0 { -+ }); allocs > expectedAllocations { - t.Errorf("expected zero allocations, got %0.1f", allocs) - } - }) -@@ -263,7 +269,7 @@ func TestAllocations(t *testing.T) { - b := []byte("ABC") - out := SumSHAKE128(b, 10) - sinkSHA3 ^= out[0] -- }); allocs > 0 { -+ }); allocs > expectedAllocations { - t.Errorf("expected zero allocations, got %0.1f", allocs) - } - }) -@@ -413,6 +419,9 @@ func testMarshalUnmarshal(t *testing.T, h *SHA3) { - h.Write(buf[:n]) - b, err := h.MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("MarshalBinary not supported") -+ } - t.Errorf("MarshalBinary: %v", err) - } - h.Write(bytes.Repeat([]byte{0}, 200)) -@@ -438,6 +447,9 @@ func testMarshalUnmarshalSHAKE(t *testing.T, h *SHAKE) { - h.Write(buf[:n]) - b, err := h.MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("MarshalBinary not supported") -+ } - t.Errorf("MarshalBinary: %v", err) - } - h.Write(bytes.Repeat([]byte{0}, 200)) -diff --git a/src/crypto/sha512/sha512.go b/src/crypto/sha512/sha512.go -index 1435eac1f5b5dc..ca424cb6b904ba 100644 ---- a/src/crypto/sha512/sha512.go -+++ b/src/crypto/sha512/sha512.go -@@ -12,7 +12,7 @@ package sha512 - - import ( - "crypto" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/sha512" - "hash" - ) -@@ -58,6 +58,9 @@ func New() hash.Hash { - // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal - // state of the hash. - func New512_224() hash.Hash { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA512_224) { -+ return boring.NewSHA512_224() -+ } - return sha512.New512_224() - } - -@@ -66,6 +69,9 @@ func New512_224() hash.Hash { - // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal - // state of the hash. - func New512_256() hash.Hash { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA512_256) { -+ return boring.NewSHA512_256() -+ } - return sha512.New512_256() - } - -@@ -106,6 +112,9 @@ func Sum384(data []byte) [Size384]byte { - - // Sum512_224 returns the Sum512/224 checksum of the data. - func Sum512_224(data []byte) [Size224]byte { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA512_224) { -+ return boring.SHA512_224(data) -+ } - h := New512_224() - h.Write(data) - var sum [Size224]byte -@@ -115,6 +124,9 @@ func Sum512_224(data []byte) [Size224]byte { - - // Sum512_256 returns the Sum512/256 checksum of the data. - func Sum512_256(data []byte) [Size256]byte { -+ if boring.Enabled && boring.SupportsHash(crypto.SHA512_256) { -+ return boring.SHA512_256(data) -+ } - h := New512_256() - h.Write(data) - var sum [Size256]byte -diff --git a/src/crypto/sha512/sha512_test.go b/src/crypto/sha512/sha512_test.go -index 080bf694f03652..389e2738a4e885 100644 ---- a/src/crypto/sha512/sha512_test.go -+++ b/src/crypto/sha512/sha512_test.go -@@ -8,9 +8,11 @@ package sha512 - - import ( - "bytes" -+ boring "crypto/internal/backend" - "crypto/internal/cryptotest" - "encoding" - "encoding/hex" -+ "errors" - "fmt" - "hash" - "io" -@@ -751,6 +753,9 @@ func testGoldenMarshal(t *testing.T) { - - state, err := h.(encoding.BinaryMarshaler).MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("could not marshal: %v", err) - return - } -@@ -807,6 +812,9 @@ func TestMarshalMismatch(t *testing.T) { - - state, err := h1.(encoding.BinaryMarshaler).MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("i=%d: could not marshal: %v", i, err) - continue - } -@@ -880,10 +888,18 @@ func safeSum(h hash.Hash) (sum []byte, err error) { - } - - func TestLargeHashes(t *testing.T) { -+ if boring.Enabled { -+ if _, ok := New().(encoding.BinaryUnmarshaler); !ok { -+ t.Skip("BinaryUnmarshaler not implemented") -+ } -+ } - for i, test := range largeUnmarshalTests { - - h := New() - if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(test.state)); err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Errorf("test %d could not unmarshal: %v", i, err) - continue - } -@@ -902,6 +918,10 @@ func TestLargeHashes(t *testing.T) { - - func TestAllocations(t *testing.T) { - cryptotest.SkipTestAllocations(t) -+ expectedAllocations := 0.0 -+ if boring.Enabled { -+ expectedAllocations = 10 -+ } - if n := testing.AllocsPerRun(10, func() { - in := []byte("hello, world!") - out := make([]byte, 0, Size) -@@ -935,8 +955,8 @@ func TestAllocations(t *testing.T) { - Sum384(in) - Sum512_224(in) - Sum512_256(in) -- }); n > 0 { -- t.Errorf("allocs = %v, want 0", n) -+ }); n > expectedAllocations { -+ t.Errorf("allocs = %v, want %v", n, expectedAllocations) - } - } - -@@ -967,25 +987,25 @@ func TestExtraMethods(t *testing.T) { - t.Run("SHA-384", func(t *testing.T) { - cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { - h := maybeCloner(New384()) -- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - }) - }) - t.Run("SHA-512/224", func(t *testing.T) { - cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { - h := maybeCloner(New512_224()) -- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - }) - }) - t.Run("SHA-512/256", func(t *testing.T) { - cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { - h := maybeCloner(New512_256()) -- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - }) - }) - t.Run("SHA-512", func(t *testing.T) { - cryptotest.TestAllImplementations(t, "sha512", func(t *testing.T) { - h := maybeCloner(New()) -- cryptotest.NoExtraMethods(t, h, "MarshalBinary", "UnmarshalBinary", "AppendBinary") -+ cryptotest.NoExtraMethods(t, &h, "MarshalBinary", "UnmarshalBinary", "AppendBinary", "WriteByte", "WriteString", "Clone") - }) - }) - } -diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go -index 1952be64fa952c..bad4a5093e3bbb 100644 ---- a/src/crypto/tls/cipher_suites.go -+++ b/src/crypto/tls/cipher_suites.go -@@ -10,7 +10,7 @@ import ( - "crypto/cipher" - "crypto/des" - "crypto/hmac" -- "crypto/internal/boring" -+ boring "crypto/internal/backend" - fipsaes "crypto/internal/fips140/aes" - "crypto/internal/fips140/aes/gcm" - "crypto/rc4" -@@ -568,7 +568,13 @@ func aeadChaCha20Poly1305(key, nonceMask []byte) aead { - if len(nonceMask) != aeadNonceLength { - panic("tls: internal error: wrong nonce length") - } -- aead, err := chacha20poly1305.New(key) -+ var aead cipher.AEAD -+ var err error -+ if boring.Enabled && boring.SupportsChaCha20Poly1305() { -+ aead, err = boring.NewChaCha20Poly1305(key) -+ } else { -+ aead, err = chacha20poly1305.New(key) -+ } - if err != nil { - panic(err) - } -diff --git a/src/crypto/tls/fipsonly/fipsonly.go b/src/crypto/tls/fipsonly/fipsonly.go -index e702f44e986746..e506a0d8841237 100644 ---- a/src/crypto/tls/fipsonly/fipsonly.go -+++ b/src/crypto/tls/fipsonly/fipsonly.go -@@ -2,7 +2,7 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - // Package fipsonly restricts all TLS configuration to FIPS-approved settings. - // -diff --git a/src/crypto/tls/fipsonly/fipsonly_test.go b/src/crypto/tls/fipsonly/fipsonly_test.go -index 027bc22c33c921..eba08da985f832 100644 ---- a/src/crypto/tls/fipsonly/fipsonly_test.go -+++ b/src/crypto/tls/fipsonly/fipsonly_test.go -@@ -2,7 +2,7 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - --//go:build boringcrypto -+//go:build goexperiment.systemcrypto - - package fipsonly - -diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go -index 52bf6087b62955..6d97d1fba0dfa9 100644 ---- a/src/crypto/tls/handshake_client.go -+++ b/src/crypto/tls/handshake_client.go -@@ -11,11 +11,11 @@ import ( - "crypto/ecdsa" - "crypto/ed25519" - "crypto/hpke" -- "crypto/internal/fips140/tls13" - "crypto/mldsa" - "crypto/rsa" - "crypto/subtle" - "crypto/tls/internal/fips140tls" -+ "crypto/tls/internal/tls13" - "crypto/x509" - "errors" - "fmt" -@@ -524,7 +524,20 @@ func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error { - - // Does the handshake, either a full one or resumes old session. Requires hs.c, - // hs.hello, hs.serverHello, and, optionally, hs.session to be set. --func (hs *clientHandshakeState) handshake() error { -+func (hs *clientHandshakeState) handshake() (err error) { -+ defer func() { -+ if err != nil { -+ return -+ } -+ if p := recover(); p != nil { -+ if err1, ok := p.(boringPRFError); ok { -+ err = err1.err -+ hs.c.sendAlert(alertInternalError) -+ } else { -+ panic(p) -+ } -+ } -+ }() - c := hs.c - - // If we did not load a session (hs.session == nil), but we did set a -diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go -index d367020c2066bf..6a83eda3573245 100644 ---- a/src/crypto/tls/handshake_client_tls13.go -+++ b/src/crypto/tls/handshake_client_tls13.go -@@ -10,9 +10,9 @@ import ( - "crypto" - "crypto/hkdf" - "crypto/hmac" -- "crypto/internal/fips140/tls13" - "crypto/rsa" - "crypto/subtle" -+ "crypto/tls/internal/tls13" - "errors" - "hash" - "slices" -diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go -index cde70463ef0fa4..4df9e6f5aac92c 100644 ---- a/src/crypto/tls/handshake_server.go -+++ b/src/crypto/tls/handshake_server.go -@@ -64,7 +64,20 @@ func (c *Conn) serverHandshake(ctx context.Context) error { - return hs.handshake() - } - --func (hs *serverHandshakeState) handshake() error { -+func (hs *serverHandshakeState) handshake() (err error) { -+ defer func() { -+ if err != nil { -+ return -+ } -+ if p := recover(); p != nil { -+ if err1, ok := p.(boringPRFError); ok { -+ err = err1.err -+ hs.c.sendAlert(alertInternalError) -+ } else { -+ panic(p) -+ } -+ } -+ }() - c := hs.c - - if err := hs.processClientHello(); err != nil { -diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go -index 3175d74587d83a..ed7c2de8db51f1 100644 ---- a/src/crypto/tls/handshake_server_tls13.go -+++ b/src/crypto/tls/handshake_server_tls13.go -@@ -11,9 +11,9 @@ import ( - "crypto/hkdf" - "crypto/hmac" - "crypto/hpke" -- "crypto/internal/fips140/tls13" - "crypto/rsa" - "crypto/tls/internal/fips140tls" -+ "crypto/tls/internal/tls13" - "crypto/x509" - "errors" - "fmt" -@@ -453,9 +453,6 @@ func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash { - UnmarshalBinary(data []byte) error - } - marshaler, ok := in.(binaryMarshaler) -- if !ok { -- return nil -- } - state, err := marshaler.MarshalBinary() - if err != nil { - return nil -diff --git a/src/crypto/tls/internal/tls13/doc.go b/src/crypto/tls/internal/tls13/doc.go -new file mode 100644 -index 00000000000000..acfa551001af9c ---- /dev/null -+++ b/src/crypto/tls/internal/tls13/doc.go -@@ -0,0 +1,18 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+/* -+Microsoft build of Go only package. -+ -+TLS 1.3 Key Schedule is normally not part of the FIPS boundary, but upstream Go -+decided to include it in the FIPS boundary to facilitate the FIPS 140-3 certification -+process. -+ -+The problem is that crypto/internal/fips140/tls13 uses crypto/internal/fips140/hkdf, -+which can't be patched to use other backends. -+ -+To solve this problem, we created this package, which is a direct copy of -+crypto/internal/fips140/tls13, but uses crypto/hkdf instead of crypto/internal/fips140/hkdf. -+*/ -+package tls13 -diff --git a/src/crypto/tls/internal/tls13/tls13.go b/src/crypto/tls/internal/tls13/tls13.go -new file mode 100644 -index 00000000000000..579aaedabb188a ---- /dev/null -+++ b/src/crypto/tls/internal/tls13/tls13.go -@@ -0,0 +1,195 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+// Package tls13 implements the TLS 1.3 Key Schedule as specified in RFC 8446, -+// Section 7.1 and allowed by FIPS 140-3 IG 2.4.B Resolution 7. -+package tls13 -+ -+import ( -+ boring "crypto/internal/backend" -+ "crypto/internal/fips140hash" -+ -+ "crypto/hkdf" -+ "hash" -+ "internal/byteorder" -+) -+ -+// We don't set the service indicator in this package but we delegate that to -+// the underlying functions because the TLS 1.3 KDF does not have a standard of -+// its own. -+ -+// ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1. -+func ExpandLabel[H hash.Hash](hash func() H, secret []byte, label string, context []byte, length int) []byte { -+ if len("tls13 ")+len(label) > 255 || len(context) > 255 { -+ // It should be impossible for this to panic: labels are fixed strings, -+ // and context is either a fixed-length computed hash, or parsed from a -+ // field which has the same length limitation. -+ // -+ // Another reasonable approach might be to return a randomized slice if -+ // we encounter an error, which would break the connection, but avoid -+ // panicking. This would perhaps be safer but significantly more -+ // confusing to users. -+ panic("tls13: label or context too long") -+ } -+ -+ if boring.Enabled && boring.SupportsTLS13KDF() { -+ fh := fips140hash.UnwrapNew(hash) -+ key, err := boring.ExpandTLS13KDF(fh, secret, []byte(label), context, length) -+ if err != nil { -+ panic(err) -+ } -+ return key -+ } -+ -+ hkdfLabel := make([]byte, 0, 2+1+len("tls13 ")+len(label)+1+len(context)) -+ hkdfLabel = byteorder.BEAppendUint16(hkdfLabel, uint16(length)) -+ hkdfLabel = append(hkdfLabel, byte(len("tls13 ")+len(label))) -+ hkdfLabel = append(hkdfLabel, "tls13 "...) -+ hkdfLabel = append(hkdfLabel, label...) -+ hkdfLabel = append(hkdfLabel, byte(len(context))) -+ hkdfLabel = append(hkdfLabel, context...) -+ key, err := hkdf.Expand(hash, secret, string(hkdfLabel), length) -+ if err != nil { -+ panic(err) -+ } -+ return key -+} -+ -+func extract[H hash.Hash](hash func() H, newSecret, currentSecret []byte) []byte { -+ if newSecret == nil { -+ newSecret = make([]byte, hash().Size()) -+ } -+ prk, err := hkdf.Extract(hash, newSecret, currentSecret) -+ if err != nil { -+ panic(err) -+ } -+ return prk -+} -+ -+func deriveSecret[H hash.Hash](hash func() H, secret []byte, label string, transcript hash.Hash) []byte { -+ if transcript == nil { -+ transcript = hash() -+ } -+ return ExpandLabel(hash, secret, label, transcript.Sum(nil), transcript.Size()) -+} -+ -+const ( -+ resumptionBinderLabel = "res binder" -+ clientEarlyTrafficLabel = "c e traffic" -+ clientHandshakeTrafficLabel = "c hs traffic" -+ serverHandshakeTrafficLabel = "s hs traffic" -+ clientApplicationTrafficLabel = "c ap traffic" -+ serverApplicationTrafficLabel = "s ap traffic" -+ earlyExporterLabel = "e exp master" -+ exporterLabel = "exp master" -+ resumptionLabel = "res master" -+) -+ -+type EarlySecret struct { -+ secret []byte -+ hash func() hash.Hash -+} -+ -+func NewEarlySecret[H hash.Hash](h func() H, psk []byte) *EarlySecret { -+ return &EarlySecret{ -+ secret: extract(h, psk, nil), -+ hash: func() hash.Hash { return h() }, -+ } -+} -+ -+func (s *EarlySecret) ResumptionBinderKey() []byte { -+ return deriveSecret(s.hash, s.secret, resumptionBinderLabel, nil) -+} -+ -+// ClientEarlyTrafficSecret derives the client_early_traffic_secret from the -+// early secret and the transcript up to the ClientHello. -+func (s *EarlySecret) ClientEarlyTrafficSecret(transcript hash.Hash) []byte { -+ return deriveSecret(s.hash, s.secret, clientEarlyTrafficLabel, transcript) -+} -+ -+type HandshakeSecret struct { -+ secret []byte -+ hash func() hash.Hash -+} -+ -+func (s *EarlySecret) HandshakeSecret(sharedSecret []byte) *HandshakeSecret { -+ derived := deriveSecret(s.hash, s.secret, "derived", nil) -+ return &HandshakeSecret{ -+ secret: extract(s.hash, sharedSecret, derived), -+ hash: s.hash, -+ } -+} -+ -+// ClientHandshakeTrafficSecret derives the client_handshake_traffic_secret from -+// the handshake secret and the transcript up to the ServerHello. -+func (s *HandshakeSecret) ClientHandshakeTrafficSecret(transcript hash.Hash) []byte { -+ return deriveSecret(s.hash, s.secret, clientHandshakeTrafficLabel, transcript) -+} -+ -+// ServerHandshakeTrafficSecret derives the server_handshake_traffic_secret from -+// the handshake secret and the transcript up to the ServerHello. -+func (s *HandshakeSecret) ServerHandshakeTrafficSecret(transcript hash.Hash) []byte { -+ return deriveSecret(s.hash, s.secret, serverHandshakeTrafficLabel, transcript) -+} -+ -+type MasterSecret struct { -+ secret []byte -+ hash func() hash.Hash -+} -+ -+func (s *HandshakeSecret) MasterSecret() *MasterSecret { -+ derived := deriveSecret(s.hash, s.secret, "derived", nil) -+ return &MasterSecret{ -+ secret: extract(s.hash, nil, derived), -+ hash: s.hash, -+ } -+} -+ -+// ClientApplicationTrafficSecret derives the client_application_traffic_secret_0 -+// from the master secret and the transcript up to the server Finished. -+func (s *MasterSecret) ClientApplicationTrafficSecret(transcript hash.Hash) []byte { -+ return deriveSecret(s.hash, s.secret, clientApplicationTrafficLabel, transcript) -+} -+ -+// ServerApplicationTrafficSecret derives the server_application_traffic_secret_0 -+// from the master secret and the transcript up to the server Finished. -+func (s *MasterSecret) ServerApplicationTrafficSecret(transcript hash.Hash) []byte { -+ return deriveSecret(s.hash, s.secret, serverApplicationTrafficLabel, transcript) -+} -+ -+// ResumptionMasterSecret derives the resumption_master_secret from the master secret -+// and the transcript up to the client Finished. -+func (s *MasterSecret) ResumptionMasterSecret(transcript hash.Hash) []byte { -+ return deriveSecret(s.hash, s.secret, resumptionLabel, transcript) -+} -+ -+type ExporterMasterSecret struct { -+ secret []byte -+ hash func() hash.Hash -+} -+ -+// ExporterMasterSecret derives the exporter_master_secret from the master secret -+// and the transcript up to the server Finished. -+func (s *MasterSecret) ExporterMasterSecret(transcript hash.Hash) *ExporterMasterSecret { -+ return &ExporterMasterSecret{ -+ secret: deriveSecret(s.hash, s.secret, exporterLabel, transcript), -+ hash: s.hash, -+ } -+} -+ -+// EarlyExporterMasterSecret derives the exporter_master_secret from the early secret -+// and the transcript up to the ClientHello. -+func (s *EarlySecret) EarlyExporterMasterSecret(transcript hash.Hash) *ExporterMasterSecret { -+ return &ExporterMasterSecret{ -+ secret: deriveSecret(s.hash, s.secret, earlyExporterLabel, transcript), -+ hash: s.hash, -+ } -+} -+ -+func (s *ExporterMasterSecret) Exporter(label string, context []byte, length int) []byte { -+ secret := deriveSecret(s.hash, s.secret, label, nil) -+ h := s.hash() -+ h.Write(context) -+ return ExpandLabel(s.hash, secret, "exporter", h.Sum(nil), length) -+} -diff --git a/src/crypto/tls/key_schedule.go b/src/crypto/tls/key_schedule.go -index 652a6489b899d6..36528e93156bef 100644 ---- a/src/crypto/tls/key_schedule.go -+++ b/src/crypto/tls/key_schedule.go -@@ -9,8 +9,8 @@ import ( - "crypto/ecdh" - "crypto/fips140" - "crypto/hmac" -- "crypto/internal/fips140/tls13" - "crypto/mlkem" -+ "crypto/tls/internal/tls13" - "errors" - "hash" - "io" -diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go -index bc59300f41c762..463ae16480f2be 100644 ---- a/src/crypto/tls/prf.go -+++ b/src/crypto/tls/prf.go -@@ -7,6 +7,7 @@ package tls - import ( - "crypto" - "crypto/hmac" -+ boring "crypto/internal/backend" - "crypto/internal/fips140/tls12" - "crypto/md5" - "crypto/sha1" -@@ -47,9 +48,25 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { - } - } - -+// boringPRFError can happen, for example, if the seed is too large. The Go implementation doesn't limit the seed size, -+// as RFC 5705 doesn't specify a limit, but stock OpenSSL restrict it to 1024 and CNG to 256. -+type boringPRFError struct { -+ err error -+} -+ -+func (e *boringPRFError) Error() string { -+ return e.err.Error() -+} -+ - // prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5. - func prf10(secret []byte, label string, seed []byte, keyLen int) []byte { - result := make([]byte, keyLen) -+ if boring.Enabled && boring.SupportsTLS1PRF() { -+ if err := boring.TLS1PRF(result, secret, []byte(label), seed, nil); err != nil { -+ panic(boringPRFError{fmt.Errorf("crypto/tls: prf10: %v", err)}) -+ } -+ return result -+ } - hashSHA1 := sha1.New - hashMD5 := md5.New - -@@ -72,6 +89,13 @@ func prf10(secret []byte, label string, seed []byte, keyLen int) []byte { - // prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. - func prf12(hashFunc func() hash.Hash) prfFunc { - return func(secret []byte, label string, seed []byte, keyLen int) []byte { -+ if boring.Enabled && boring.SupportsTLS1PRF() { -+ result := make([]byte, keyLen) -+ if err := boring.TLS1PRF(result, secret, []byte(label), seed, hashFunc); err != nil { -+ panic(boringPRFError{fmt.Errorf("crypto/tls: prf12: %v", err)}) -+ } -+ return result -+ } - return tls12.PRF(hashFunc, secret, label, seed, keyLen) - } - } -diff --git a/src/crypto/tls/prf_test.go b/src/crypto/tls/prf_test.go -index 8233985a62bd22..ef1b2bfe02c3b7 100644 ---- a/src/crypto/tls/prf_test.go -+++ b/src/crypto/tls/prf_test.go -@@ -5,7 +5,10 @@ - package tls - - import ( -+ "crypto/fips140" -+ boring "crypto/internal/backend" - "encoding/hex" -+ "runtime" - "testing" - ) - -@@ -46,6 +49,12 @@ type testKeysFromTest struct { - } - - func TestKeysFromPreMasterSecret(t *testing.T) { -+ if boring.Enabled && runtime.GOOS == "linux" && fips140.Enabled() { -+ // Some OpenSSL providers don't support TLS 1.0 PRF in FIPS mode. -+ // The public crypto/tls API doesn't support it either, but these tests -+ // use internal functions. -+ t.Skip("Skipping test on systemcrypto FIPS builds") -+ } - for i, test := range testKeysFromTests { - in, _ := hex.DecodeString(test.preMasterSecret) - clientRandom, _ := hex.DecodeString(test.clientRandom) -diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go -index 1d3e845d0f0a9c..eb4318faf13f7b 100644 ---- a/src/crypto/x509/verify_test.go -+++ b/src/crypto/x509/verify_test.go -@@ -3127,7 +3127,7 @@ func dsaSelfSignedCNX(t *testing.T) []byte { - t.Helper() - var params dsa.Parameters - if err := dsa.GenerateParameters(¶ms, rand.Reader, dsa.L1024N160); err != nil { -- t.Fatal(err) -+ t.Skip(err) // CBL-Mariner doesn't support dsa.L1024N160, skip test - } - - var dsaPriv dsa.PrivateKey -diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index 7794e12bc86890..d6ef0bf10ebc1c 100644 ---- a/src/go/build/deps_test.go -+++ b/src/go/build/deps_test.go -@@ -533,7 +533,7 @@ var depsRules = ` - < crypto/internal/fips140/edwards25519 - < crypto/internal/fips140/ed25519 - < crypto/internal/fips140/rsa -- < crypto/fips140 < FIPS; -+ < FIPS; - - crypto !< FIPS; - -@@ -557,7 +557,12 @@ var depsRules = ` - < crypto/internal/backend/internal/opensslsetup - < crypto/internal/backend/fips140; - -- FIPS, internal/godebug, embed -+ internal/godebug, crypto/internal/fips140, crypto/internal/fips140/check, -+ crypto/internal/backend/fips140 -+ < crypto/fips140; -+ -+ FIPS, internal/godebug, embed, -+ crypto/fips140 - < crypto/internal/fips140only - < crypto - < crypto/subtle -@@ -591,7 +596,8 @@ var depsRules = ` - crypto/internal/fips140only, - crypto, - crypto/subtle, -- crypto/cipher -+ crypto/cipher, -+ crypto/internal/backend - < crypto/sha3 - < crypto/internal/fips140hash - < crypto/internal/boring -@@ -610,6 +616,7 @@ var depsRules = ` - crypto/ecdh, - crypto/mlkem, - crypto/mldsa -+ < crypto/tls/internal/tls13 - < CRYPTO; - - CRYPTO -diff --git a/src/hash/boring_test.go b/src/hash/boring_test.go -new file mode 100644 -index 00000000000000..52748c44698076 ---- /dev/null -+++ b/src/hash/boring_test.go -@@ -0,0 +1,9 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build goexperiment.systemcrypto -+ -+package hash_test -+ -+const boringEnabled = true -diff --git a/src/hash/example_test.go b/src/hash/example_test.go -index f07b9aaa2c4898..b380537215634d 100644 ---- a/src/hash/example_test.go -+++ b/src/hash/example_test.go -@@ -2,6 +2,8 @@ - // Use of this source code is governed by a BSD-style - // license that can be found in the LICENSE file. - -+//go:build !goexperiment.systemcrypto -+ - package hash_test - - import ( -diff --git a/src/hash/marshal_test.go b/src/hash/marshal_test.go -index 3091f7a67acede..fa3f0fd82cfd30 100644 ---- a/src/hash/marshal_test.go -+++ b/src/hash/marshal_test.go -@@ -16,6 +16,7 @@ import ( - "crypto/sha512" - "encoding" - "encoding/hex" -+ "errors" - "hash" - "hash/adler32" - "hash/crc32" -@@ -80,6 +81,9 @@ func TestMarshalHash(t *testing.T) { - } - enc, err := h2m.MarshalBinary() - if err != nil { -+ if errors.Is(err, errors.ErrUnsupported) { -+ t.Skip("BinaryMarshaler not supported") -+ } - t.Fatalf("MarshalBinary: %v", err) - } - if !bytes.Equal(enc, tt.golden) { -diff --git a/src/hash/notboring_test.go b/src/hash/notboring_test.go -new file mode 100644 -index 00000000000000..11dc691600b110 ---- /dev/null -+++ b/src/hash/notboring_test.go -@@ -0,0 +1,9 @@ -+// Copyright 2024 The Go Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style -+// license that can be found in the LICENSE file. -+ -+//go:build !goexperiment.systemcrypto -+ -+package hash_test -+ -+const boringEnabled = false -diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go -index 42211ed099ed1e..e6d185374ffe6d 100644 ---- a/src/net/lookup_test.go -+++ b/src/net/lookup_test.go -@@ -1500,6 +1500,9 @@ func TestLookupPortIPNetworkString(t *testing.T) { - } - - func TestLookupNoSuchHost(t *testing.T) { -+ if runtime.GOOS == "darwin" { -+ t.Skip("skipping on darwin; see https://github.com/microsoft/go/issues/1394") -+ } - mustHaveExternalNetwork(t) - - const testNXDOMAIN = "invalid.invalid." -diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go -index 4f39d9bbe345a8..0a379aaf9c1859 100644 ---- a/src/os/exec/exec_test.go -+++ b/src/os/exec/exec_test.go -@@ -14,6 +14,7 @@ import ( - "errors" - "flag" - "fmt" -+ "internal/goexperiment" - "internal/poll" - "internal/testenv" - "io" -@@ -692,6 +693,14 @@ func TestExtraFiles(t *testing.T) { - t.Skipf("skipping test on %q", runtime.GOOS) - } - -+ if goexperiment.SystemCrypto && runtime.GOOS == "linux" { -+ // OpenSSL default behavior is to maintain open FDs to any -+ // random devices that get used by the random number library. -+ // Since those FDs are not marked FD_CLOEXEC or O_CLOEXEC, -+ // they also get inherited by children. -+ t.Skip("skipping test because test was run with systemcrypto on linux (OpenSSL)") -+ } -+ - // Force network usage, to verify the epoll (or whatever) fd - // doesn't leak to the child, - ln, err := net.Listen("tcp", "127.0.0.1:0") diff --git a/patches/0007-Remove-long-path-support-hack.patch b/patches/0005-Remove-long-path-support-hack.patch similarity index 100% rename from patches/0007-Remove-long-path-support-hack.patch rename to patches/0005-Remove-long-path-support-hack.patch diff --git a/patches/0008-Omit-internal-go.mod-files-used-for-codegen.patch b/patches/0006-Omit-internal-go.mod-files-used-for-codegen.patch similarity index 100% rename from patches/0008-Omit-internal-go.mod-files-used-for-codegen.patch rename to patches/0006-Omit-internal-go.mod-files-used-for-codegen.patch diff --git a/patches/0009-Add-appinsights-telemetry.patch b/patches/0007-Add-appinsights-telemetry.patch similarity index 77% rename from patches/0009-Add-appinsights-telemetry.patch rename to patches/0007-Add-appinsights-telemetry.patch index 9bb8d64efb1..1e48c16b69b 100644 --- a/patches/0009-Add-appinsights-telemetry.patch +++ b/patches/0007-Add-appinsights-telemetry.patch @@ -5,14 +5,14 @@ Subject: [PATCH] Add appinsights telemetry --- README.md | 15 +++++ - .../internal/telemetrystats/telemetrystats.go | 10 ++++ - src/cmd/go/main.go | 8 +++ - src/cmd/go/mstelemetry_test.go | 56 +++++++++++++++++++ - src/cmd/go/script_test.go | 10 ++++ - src/cmd/go/scriptcmds_test.go | 22 ++++++++ - src/cmd/internal/telemetry/counter/counter.go | 54 +++++++++++++++++- - .../telemetry/counter/counter_bootstrap.go | 3 + - 8 files changed, 177 insertions(+), 1 deletion(-) + .../internal/telemetrystats/telemetrystats.go | 17 ++++++ + src/cmd/go/main.go | 28 ++++++++- + src/cmd/go/mstelemetry_test.go | 56 +++++++++++++++++ + src/cmd/go/script_test.go | 10 +++ + src/cmd/go/scriptcmds_test.go | 22 +++++++ + src/cmd/internal/telemetry/counter/counter.go | 61 ++++++++++++++++++- + .../telemetry/counter/counter_bootstrap.go | 10 ++- + 8 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 src/cmd/go/mstelemetry_test.go diff --git a/README.md b/README.md @@ -40,27 +40,35 @@ index 71c9d1dc299388..8cab5daec4da54 100644 +these practices. \ No newline at end of file diff --git a/src/cmd/go/internal/telemetrystats/telemetrystats.go b/src/cmd/go/internal/telemetrystats/telemetrystats.go -index c6e58935bb0ee2..5aec1694aaae96 100644 +index c6e58935bb0ee2..82651441fe61fd 100644 --- a/src/cmd/go/internal/telemetrystats/telemetrystats.go +++ b/src/cmd/go/internal/telemetrystats/telemetrystats.go -@@ -11,6 +11,8 @@ import ( +@@ -11,7 +11,12 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/modload" "cmd/internal/telemetry/counter" -+ "internal/buildcfg" + "internal/platform" ++ "internal/systemcrypto" ++ "os" "strings" ++ ++ "github.com/microsoft/go-infra/telemetry" ) -@@ -39,6 +41,14 @@ func incrementConfig() { + func Increment() { +@@ -39,6 +44,18 @@ func incrementConfig() { counter.Inc("go/cgo:disabled") } ++ if ci := telemetry.DetectCI(os.Environ()); ci != "" { ++ counter.Inc("msgo/ci:" + ci) ++ } ++ + if platform.SystemCryptoSupported(cfg.Goos, cfg.Goarch) { -+ if buildcfg.SystemCryptoDisabled { -+ counter.Inc("msgo/systemcrypto:disabled") -+ } else { ++ if systemcrypto.EnabledFor(cfg.Goos, cfg.Goarch) { + counter.Inc("msgo/systemcrypto:enabled") ++ } else { ++ counter.Inc("msgo/systemcrypto:disabled") + } + } + @@ -68,13 +76,33 @@ index c6e58935bb0ee2..5aec1694aaae96 100644 counter.Inc("go/platform/target/goarch:" + cfg.Goarch) counter.Inc("go/platform/target/port:" + cfg.Goos + "-" + cfg.Goarch) diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go -index 8cdfd9196e4cb1..3f4a4d8f912969 100644 +index bba11a2fdeaaa6..641f8ea631d992 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go -@@ -110,6 +110,14 @@ func main() { +@@ -8,6 +8,8 @@ package main + + import ( + "context" ++ "crypto/sha256" ++ "encoding/hex" + "flag" + "fmt" + "internal/buildcfg" +@@ -25,7 +27,9 @@ import ( + "cmd/go/internal/doc" + "cmd/go/internal/envcmd" + "cmd/go/internal/fmtcmd" ++ "cmd/go/internal/fsys" + "cmd/go/internal/generate" ++ "cmd/go/internal/gover" + "cmd/go/internal/help" + "cmd/go/internal/list" + "cmd/go/internal/modcmd" +@@ -110,7 +114,29 @@ func main() { } flag.Usage = base.Usage flag.Parse() +- counter.Inc("go/invocations") + if args := flag.Args(); len(args) > 0 { + switch args[0] { + case "build", "install", "run": @@ -83,9 +111,24 @@ index 8cdfd9196e4cb1..3f4a4d8f912969 100644 + base.AtExit(counter.CloseMicrosoft) + } + } - counter.Inc("go/invocations") ++ // Send a SHA-256 hash of the module path as a property on go/invocations. ++ var moduleHash string ++ if gomod := modload.FindGoMod(base.Cwd()); gomod != "" { ++ if data, err := fsys.ReadFile(gomod); err == nil { ++ if mod := gover.GoModLookup(data, "module"); mod != "" { ++ h := sha256.Sum256([]byte(mod)) ++ moduleHash = hex.EncodeToString(h[:]) ++ } ++ } ++ } ++ if moduleHash != "" { ++ counter.IncWithProperties("go/invocations", map[string]string{"msgo/module/hash": moduleHash}) ++ } else { ++ counter.Inc("go/invocations") ++ } counter.CountFlags("go/flag:", *flag.CommandLine) + args := flag.Args() diff --git a/src/cmd/go/mstelemetry_test.go b/src/cmd/go/mstelemetry_test.go new file mode 100644 index 00000000000000..010270fb6ad4cf @@ -224,7 +267,7 @@ index 8195e830caa31b..882aeecfa562b9 100644 }) } diff --git a/src/cmd/internal/telemetry/counter/counter.go b/src/cmd/internal/telemetry/counter/counter.go -index 5cef0b0041551a..8c2bae0e613c92 100644 +index 5cef0b0041551a..a81daf56c9e991 100644 --- a/src/cmd/internal/telemetry/counter/counter.go +++ b/src/cmd/internal/telemetry/counter/counter.go @@ -7,10 +7,24 @@ @@ -252,7 +295,7 @@ index 5cef0b0041551a..8c2bae0e613c92 100644 ) var openCalled bool -@@ -24,9 +38,44 @@ func Open() { +@@ -24,9 +38,51 @@ func Open() { counter.OpenDir(os.Getenv("TEST_TELEMETRY_DIR")) } @@ -294,10 +337,17 @@ index 5cef0b0041551a..8c2bae0e613c92 100644 func Inc(name string) { counter.Inc(name) + mscounter.Inc(name) ++} ++ ++// IncWithProperties increments the counter with the given name and custom properties. ++// Properties are sent as custom dimensions in Application Insights. ++func IncWithProperties(name string, properties map[string]string) { ++ counter.Inc(name) ++ mscounter.NewWithProperties(name, properties).Inc() } // New returns a counter with the given name. -@@ -44,6 +93,7 @@ func NewStack(name string, depth int) *counter.StackCounter { +@@ -44,6 +100,7 @@ func NewStack(name string, depth int) *counter.StackCounter { // the concatenation of prefix and the flag name. func CountFlags(prefix string, flagSet flag.FlagSet) { counter.CountFlags(prefix, flagSet) @@ -305,7 +355,7 @@ index 5cef0b0041551a..8c2bae0e613c92 100644 } // CountFlagValue creates a counter for the flag value -@@ -56,7 +106,9 @@ func CountFlagValue(prefix string, flagSet flag.FlagSet, flagName string) { +@@ -56,7 +113,9 @@ func CountFlagValue(prefix string, flagSet flag.FlagSet, flagName string) { // TODO(matloob): Add this to x/telemetry? flagSet.Visit(func(f *flag.Flag) { if f.Name == flagName { @@ -317,10 +367,20 @@ index 5cef0b0041551a..8c2bae0e613c92 100644 }) } diff --git a/src/cmd/internal/telemetry/counter/counter_bootstrap.go b/src/cmd/internal/telemetry/counter/counter_bootstrap.go -index 00808294053c23..e19970cf926e16 100644 +index 00808294053c23..0780b24792c795 100644 --- a/src/cmd/internal/telemetry/counter/counter_bootstrap.go +++ b/src/cmd/internal/telemetry/counter/counter_bootstrap.go -@@ -18,3 +18,6 @@ func New(name string) dummyCounter { retu +@@ -12,9 +12,13 @@ type dummyCounter struct{} + + func (dc dummyCounter) Inc() {} + +-func Open() {} +-func Inc(name string) {} +-func New(name string) dummyCounter { return dummyCounter{} } ++func Open() {} ++func Inc(name string) {} ++func IncWithProperties(name string, properties map[string]string) {} ++func New(name string) dummyCounter { return dummyCounter{} } func NewStack(name string, depth int) dummyCounter { return dummyCounter{} } func CountFlags(name string, flagSet flag.FlagSet) {} func CountFlagValue(prefix string, flagSet flag.FlagSet, flagName string) {} diff --git a/patches/0010-Implement-ms_tls_config_schannel-experiment.patch b/patches/0008-Implement-ms_tls_config_schannel-experiment.patch similarity index 100% rename from patches/0010-Implement-ms_tls_config_schannel-experiment.patch rename to patches/0008-Implement-ms_tls_config_schannel-experiment.patch diff --git a/patches/0011-Set-ms-go-build-as-default-GOCACHE.patch b/patches/0009-Set-ms-go-build-as-default-GOCACHE.patch similarity index 100% rename from patches/0011-Set-ms-go-build-as-default-GOCACHE.patch rename to patches/0009-Set-ms-go-build-as-default-GOCACHE.patch diff --git a/patches/0012-Align-TLS-settings-with-Microsoft-policies.patch b/patches/0010-Align-TLS-settings-with-Microsoft-policies.patch similarity index 100% rename from patches/0012-Align-TLS-settings-with-Microsoft-policies.patch rename to patches/0010-Align-TLS-settings-with-Microsoft-policies.patch diff --git a/patches/0013-Add-Microsoft-version-information.patch b/patches/0011-Add-Microsoft-version-information.patch similarity index 96% rename from patches/0013-Add-Microsoft-version-information.patch rename to patches/0011-Add-Microsoft-version-information.patch index f8ae5f87aee..7c03c5ceb38 100644 --- a/patches/0013-Add-Microsoft-version-information.patch +++ b/patches/0011-Add-Microsoft-version-information.patch @@ -31,10 +31,10 @@ Microsoft build of Go version string. create mode 100644 src/cmd/go/testdata/script/gotoolchain_local_ms.txt diff --git a/doc/godebug.md b/doc/godebug.md -index 1736fd71017add..114079d8f99527 100644 +index 4701102b91ec93..f6f8d9a37b138a 100644 --- a/doc/godebug.md +++ b/doc/godebug.md -@@ -216,6 +216,15 @@ Microsoft build of Go 1.26 added a new `ms_tlsprofile` setting that controls the +@@ -236,6 +236,15 @@ Microsoft build of Go 1.26 added a new `ms_tlsprofile` setting that controls the - "default": use the recommended Microsoft TLS settings. This is the default. - "off": use the default upstream Go TLS settings. @@ -51,7 +51,7 @@ index 1736fd71017add..114079d8f99527 100644 Go 1.25 added a new `decoratemappings` setting that controls whether the Go diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go -index 6a1a5258b22dc8..0d6b05a42920bb 100644 +index decf356a449ba9..270607af75229e 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -455,6 +455,22 @@ func findgoversion() string { @@ -264,7 +264,7 @@ index 27d275644a4bd0..3c40024b4b7ab7 100644 } if i := strings.Index(version, "-X:"); i >= 0 { diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go -index 0d5d4fe0556564..78cd9a2680b03f 100644 +index ba582cf1d0773e..ca91bd97545057 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -14,6 +14,7 @@ import ( @@ -274,9 +274,9 @@ index 0d5d4fe0556564..78cd9a2680b03f 100644 + "internal/buildcfg" "internal/godebug" "internal/platform" - "io/fs" -@@ -2420,6 +2421,7 @@ func (p *Package) setBuildInfo(ctx context.Context, f *modfetch.Fetcher, autoVCS - if cfg.Experiment.SystemCrypto { + "internal/systemcrypto" +@@ -2421,6 +2422,7 @@ func (p *Package) setBuildInfo(ctx context.Context, f *modfetch.Fetcher, autoVCS + if systemcrypto.EnabledFor(cfg.Goos, cfg.Goarch) { appendSetting("microsoft_systemcrypto", "1") } + appendSetting("microsoft_toolset_version", buildcfg.Version) @@ -336,7 +336,7 @@ index 00000000000000..24538695be2cf2 +go version +stdout go1.501 diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go -index 5e592c7c6697d7..df70274e03b5df 100644 +index 19ed197a20b57e..92672cc5bb60dc 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -188,15 +188,20 @@ func Main(arch *sys.Arch, theArch Arch) { @@ -348,7 +348,7 @@ index 5e592c7c6697d7..df70274e03b5df 100644 + // versions. This approach is intended to minimize the patch diff and avoid + // touching the same lines as earlier patches. + appendExperimentVersion := func(buildVersion string) string { return buildVersion } - if goexperiment := buildcfg.Experiment.MSString(); goexperiment != "" { + if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { - sep := " " - if !strings.Contains(buildVersion, "-") { // See go.dev/issue/75953. - sep = "-" @@ -447,10 +447,10 @@ index 6d266ff641d721..d1ed759065e278 100644 var Error error diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go -index 1ba17bc136710f..a3868ad860ed4a 100644 +index 360c0f82e1264e..4aee5045a9efac 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go -@@ -51,6 +51,7 @@ var All = []Info{ +@@ -50,6 +50,7 @@ var All = []Info{ {Name: "jstmpllitinterp", Package: "html/template", Opaque: true}, // bug #66217: remove Opaque {Name: "ms_tlsprofile", Package: "crypto/tls", Opaque: true}, {Name: "ms_tlsx25519", Package: "crypto/tls", Opaque: true}, @@ -499,10 +499,10 @@ index a69f4aaede794d..f5c35867b78e26 100644 // GOOS is the running program's operating system target: diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go -index 835aa8ecedb484..78c2673fbe3661 100644 +index 0cc653b0a3d156..145c91337ddab6 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go -@@ -361,6 +361,9 @@ var debug struct { +@@ -351,6 +351,9 @@ var debug struct { // tracebacklabels controls the inclusion of goroutine labels in the // goroutine status header line. tracebacklabels atomic.Int32 @@ -512,7 +512,7 @@ index 835aa8ecedb484..78c2673fbe3661 100644 } var dbgvars = []*dbgVar{ -@@ -398,6 +401,7 @@ var dbgvars = []*dbgVar{ +@@ -387,6 +390,7 @@ var dbgvars = []*dbgVar{ {name: "tracebacklabels", atomic: &debug.tracebacklabels, def: 1}, {name: "tracefpunwindoff", value: &debug.tracefpunwindoff}, {name: "updatemaxprocs", value: &debug.updatemaxprocs, def: 1}, diff --git a/patches/0014-skip-sanitizer-tests.patch b/patches/0014-skip-sanitizer-tests.patch deleted file mode 100644 index a962a86f454..00000000000 --- a/patches/0014-skip-sanitizer-tests.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: bot-for-go[bot] <199222863+bot-for-go[bot]@users.noreply.github.com> -Date: Fri, 13 Feb 2026 16:58:59 +0100 -Subject: [PATCH] skip sanitizer tests - -Skipping sanitizer tests until they are less flaky. -See https://github.com/golang/go/issues/72996. ---- - src/cmd/cgo/internal/testsanitizers/asan_test.go | 1 + - src/cmd/cgo/internal/testsanitizers/lsan_test.go | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/src/cmd/cgo/internal/testsanitizers/asan_test.go b/src/cmd/cgo/internal/testsanitizers/asan_test.go -index cb7d857280416f..3c6aa4a324f7e7 100644 ---- a/src/cmd/cgo/internal/testsanitizers/asan_test.go -+++ b/src/cmd/cgo/internal/testsanitizers/asan_test.go -@@ -144,6 +144,7 @@ func TestASANFuzz(t *testing.T) { - } - - func mustHaveASAN(t *testing.T) *config { -+ t.Skip("skipping sanitizer tests until we have a better way to reliably test it in CI. See https://github.com/golang/go/issues/72996.") - testenv.MustHaveGoBuild(t) - testenv.MustHaveCGO(t) - goos, err := goEnv("GOOS") -diff --git a/src/cmd/cgo/internal/testsanitizers/lsan_test.go b/src/cmd/cgo/internal/testsanitizers/lsan_test.go -index 4dde3d20eca038..0e0ed2ffab8c3f 100644 ---- a/src/cmd/cgo/internal/testsanitizers/lsan_test.go -+++ b/src/cmd/cgo/internal/testsanitizers/lsan_test.go -@@ -74,6 +74,7 @@ func TestLSAN(t *testing.T) { - } - - func mustHaveLSAN(t *testing.T) *config { -+ t.Skip("skipping sanitizer tests until we have a better way to reliably test it in CI. See https://github.com/golang/go/issues/72996.") - testenv.MustHaveGoBuild(t) - testenv.MustHaveCGO(t) - goos, err := goEnv("GOOS") diff --git a/patches/0015-Skip-http2-tests.patch b/patches/0015-Skip-http2-tests.patch deleted file mode 100644 index b88d6369006..00000000000 --- a/patches/0015-Skip-http2-tests.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: bot-for-go[bot] <199222863+bot-for-go[bot]@users.noreply.github.com> -Date: Wed, 8 Apr 2026 12:15:13 +0200 -Subject: [PATCH] Skip http2 tests - -http2 tests can't be run in FIPS mode due to a weird interaction between -crypto and test/synctest bubbles. - -Tracked at https://github.com/golang/go/issues/78557 ---- - src/net/http/internal/http2/http2_test.go | 10 ++++++++++ - src/net/http/serve_test.go | 5 +++++ - src/net/http/transport_test.go | 5 +++++ - 3 files changed, 20 insertions(+) - -diff --git a/src/net/http/internal/http2/http2_test.go b/src/net/http/internal/http2/http2_test.go -index ad1b9a6d06e773..7e7042796a12d8 100644 ---- a/src/net/http/internal/http2/http2_test.go -+++ b/src/net/http/internal/http2/http2_test.go -@@ -5,6 +5,7 @@ - package http2 - - import ( -+ "crypto/fips140" - "flag" - "fmt" - "os" -@@ -28,6 +29,15 @@ func init() { - flag.BoolVar(&VerboseLogs, "verboseh2", VerboseLogs, "Verbose HTTP/2 debug logging") - } - -+func TestMain(m *testing.M) { -+ if fips140.Enabled() { -+ // TODO: Remove once https://github.com/golang/go/issues/78557 is resolved. -+ os.Exit(0) -+ } -+ -+ os.Exit(m.Run()) -+} -+ - func TestSettingString(t *testing.T) { - tests := []struct { - s Setting -diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go -index 7e8398485ab4df..e3e37ffba73230 100644 ---- a/src/net/http/serve_test.go -+++ b/src/net/http/serve_test.go -@@ -12,6 +12,7 @@ import ( - "compress/gzip" - "compress/zlib" - "context" -+ "crypto/fips140" - crand "crypto/rand" - "crypto/tls" - "crypto/x509" -@@ -6078,6 +6079,10 @@ func TestServerCloseDeadlock(t *testing.T) { - // Issue 17717: tests that Server.SetKeepAlivesEnabled is respected by - // both HTTP/1 and HTTP/2. - func TestServerKeepAlivesEnabled(t *testing.T) { -+ if fips140.Enabled() { -+ // TODO: Remove once https://github.com/golang/go/issues/78557 is resolved. -+ t.Skip("skipping test in FIPS 140 mode, see https://github.com/golang/go/issues/78557") -+ } - runSynctest(t, testServerKeepAlivesEnabled, http3SkippedMode) - } - func testServerKeepAlivesEnabled(t *testing.T, mode testMode) { -diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go -index 30d6d13bdd0a97..8ef27157b55ce0 100644 ---- a/src/net/http/transport_test.go -+++ b/src/net/http/transport_test.go -@@ -14,6 +14,7 @@ import ( - "bytes" - "compress/gzip" - "context" -+ "crypto/fips140" - "crypto/rand" - "crypto/tls" - "crypto/x509" -@@ -474,6 +475,10 @@ func testTransportReadToEndReusesConn(t *testing.T, mode testMode) { - // manage to drain it before the next request, the connection is re-used; - // otherwise, a new connection is made. - func TestTransportNotReadToEndConnectionReuse(t *testing.T) { -+ if fips140.Enabled() { -+ // TODO: Remove once https://github.com/golang/go/issues/78557 is resolved. -+ t.Skip("skipping test in FIPS 140 mode, see https://github.com/golang/go/issues/78557") -+ } - run(t, testTransportNotReadToEndConnectionReuse, []testMode{http1Mode, https1Mode}) - } - func testTransportNotReadToEndConnectionReuse(t *testing.T, mode testMode) {