From cb8a4353dadbb7bc94af993d5428a1212689b3ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 10:40:03 +0000 Subject: [PATCH 01/36] build(deps): bump golang.org/x/net from 0.54.0 to 0.55.0 in /eng/_util Bumps [golang.org/x/net](https://github.com/golang/net) from 0.54.0 to 0.55.0. - [Commits](https://github.com/golang/net/compare/v0.54.0...v0.55.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.55.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- eng/_util/go.mod | 2 +- eng/_util/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/_util/go.mod b/eng/_util/go.mod index 353a3db41f..98c2d1bf81 100644 --- a/eng/_util/go.mod +++ b/eng/_util/go.mod @@ -10,7 +10,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.3.1 github.com/microsoft/go-infra v0.0.13 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 318abc4e41..73f6a2b3ea 100644 --- a/eng/_util/go.sum +++ b/eng/_util/go.sum @@ -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= From 081f267a01389e9e34f8be2d0e9824e73c969b56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 10:40:34 +0000 Subject: [PATCH 02/36] build(deps): bump microsoft/go-infra/.github/workflows/check-line-endings.yml Bumps [microsoft/go-infra/.github/workflows/check-line-endings.yml](https://github.com/microsoft/go-infra) from 0.0.13 to 0.0.14. - [Release notes](https://github.com/microsoft/go-infra/releases) - [Commits](https://github.com/microsoft/go-infra/compare/f4e3833067a0e64dbf9581e3e4d2df2a353f6dd0...eae52708dd530100eb695850bfc7f994dd9340d3) --- updated-dependencies: - dependency-name: microsoft/go-infra/.github/workflows/check-line-endings.yml dependency-version: 0.0.14 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/check-line-endings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-line-endings.yml b/.github/workflows/check-line-endings.yml index ec9c12e13e..faa5f00a59 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 From 6764c85ca8d5d9cefc0eac52a9faede1565a4ad5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 11:52:19 +0000 Subject: [PATCH 03/36] build(deps): bump github.com/microsoft/go-infra in /eng/_util Bumps [github.com/microsoft/go-infra](https://github.com/microsoft/go-infra) from 0.0.13 to 0.0.14. - [Release notes](https://github.com/microsoft/go-infra/releases) - [Commits](https://github.com/microsoft/go-infra/compare/v0.0.13...v0.0.14) --- updated-dependencies: - dependency-name: github.com/microsoft/go-infra dependency-version: 0.0.14 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- eng/_util/go.mod | 2 +- eng/_util/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/_util/go.mod b/eng/_util/go.mod index 98c2d1bf81..e9329ee150 100644 --- a/eng/_util/go.mod +++ b/eng/_util/go.mod @@ -8,7 +8,7 @@ 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.55.0 ) diff --git a/eng/_util/go.sum b/eng/_util/go.sum index 73f6a2b3ea..0b092108aa 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= From 29843e3e2d9ba48de5f7b9cf994029a3b0fe7aab Mon Sep 17 00:00:00 2001 From: "bot-for-go[bot]" <199222863+bot-for-go[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 16:03:44 +0000 Subject: [PATCH 04/36] Update submodule to latest master (cc623858): runtime/pprof: update test's expected frame count --- go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go b/go index 04ed01963e..cc623858f6 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit 04ed01963e1b2ec2f801416988cb7d57b7e13b36 +Subproject commit cc623858f6c58767931f381b3ab3821228d9dc17 From bba93ff36cb16c18d86e51610a59704c622a55a9 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 25 May 2026 10:39:25 +0200 Subject: [PATCH 05/36] fix conflicts --- patches/0001-Vendor-external-dependencies.patch | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/patches/0001-Vendor-external-dependencies.patch b/patches/0001-Vendor-external-dependencies.patch index 3411b570fc..7bcc747a11 100644 --- a/patches/0001-Vendor-external-dependencies.patch +++ b/patches/0001-Vendor-external-dependencies.patch @@ -527,7 +527,7 @@ 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 29b78693abd787..dbc87f66957dcb 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -4,6 +4,8 @@ go 1.27 @@ -536,11 +536,11 @@ index df4fba11865b84..c16e08c45e76b8 100644 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/arch v0.27.1-0.20260521044007-9c1a596a2c97 golang.org/x/build v0.0.0-20260122183339-3ba88df37c64 golang.org/x/mod v0.36.1-0.20260513122029-343ee60345a1 diff --git a/src/cmd/go.sum b/src/cmd/go.sum -index b8531a556a60ab..4173afb82bbd89 100644 +index 929e1c34dcbec1..b873d102f0a9f7 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 @@ -553,7 +553,7 @@ index b8531a556a60ab..4173afb82bbd89 100644 +github.com/microsoft/go-infra/telemetry/config v0.0.0-20260513084116-8db604e72b76/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 @@ -2220,7 +2220,7 @@ index 00000000000000..e50deaa4189b18 + return false +} diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt -index b8a8f795ec2475..0844d267c939ea 100644 +index f3ed9c985e7bf5..96de94b746e601 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -16,6 +16,17 @@ github.com/google/pprof/third_party/svgpan @@ -2238,7 +2238,7 @@ index b8a8f795ec2475..0844d267c939ea 100644 +# github.com/microsoft/go-infra/telemetry/config v0.0.0-20260513084116-8db604e72b76 +## 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 @@ -2298,7 +2298,7 @@ index 43554ff45c183e..01f4f244d6f7be 100644 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= 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 +2397,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{ From 6ffd85c30da3272c2ffba0a39662ac7090e20c9e Mon Sep 17 00:00:00 2001 From: "bot-for-go[bot]" <199222863+bot-for-go[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 16:21:45 +0000 Subject: [PATCH 06/36] Update submodule to latest master (9da73fb7): cmd/go/internal/envcmd: print a default value for GOBIN --- go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go b/go index cc623858f6..9da73fb702 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit cc623858f6c58767931f381b3ab3821228d9dc17 +Subproject commit 9da73fb7025cd64b71608241517d6cb17735d854 From 040e938b44ce660ce59c0c418437c4319282ed75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 21:34:36 +0100 Subject: [PATCH 07/36] build(deps): bump github/codeql-action from 4.35.5 to 4.36.0 (#2311) --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 110248f748..334cc5409f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,16 +23,16 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Initialize CodeQL - uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: working-directory: eng/_util - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: category: /language:go From 466d3f4dc43426eb8079b1eefc631bfbb689af2b Mon Sep 17 00:00:00 2001 From: George Adams Date: Tue, 26 May 2026 08:30:30 +0100 Subject: [PATCH 08/36] resolve conflicts --- patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch index 2132d88123..d8a2206551 100644 --- a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch +++ b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch @@ -61,10 +61,10 @@ index 74cafe9613917d..31ce076273aba2 100644 // 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 +index 3d72c368b088a2..4fe28740a6e3c2 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 { +@@ -124,6 +124,7 @@ func MkEnv() []cfg.EnvVar { {Name: "GOTOOLDIR", Value: build.ToolDir}, {Name: "GOVCS", Value: cfg.GOVCS}, {Name: "GOVERSION", Value: runtime.Version()}, @@ -72,12 +72,12 @@ index ed23b856f132c6..58ce4adbc4e394 100644 } for i := range env { -@@ -133,7 +134,7 @@ func MkEnv() []cfg.EnvVar { +@@ -132,7 +133,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": +- 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 } From eb04310c4c147db8f771d5be7c3c5a03280648d9 Mon Sep 17 00:00:00 2001 From: George Adams Date: Tue, 26 May 2026 10:24:24 +0100 Subject: [PATCH 09/36] telemetry: add msgo/ci counter (#2314) * telemetry: add msgo/ci counter * remove legacy counters --- eng/doc/AdditionalFeatures.md | 3 +- eng/doc/MigrationGuide.md | 3 +- eng/doc/README.md | 1 + eng/doc/Telemetry.md | 75 +++++++++ .../0001-Vendor-external-dependencies.patch | 144 +++++++++++++++--- patches/0009-Add-appinsights-telemetry.patch | 20 ++- 6 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 eng/doc/Telemetry.md diff --git a/eng/doc/AdditionalFeatures.md b/eng/doc/AdditionalFeatures.md index b109c7aab8..f6081880aa 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 c4918c6351..2792447ea4 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 aaed7c9848..c7641610b3 100644 --- a/eng/doc/README.md +++ b/eng/doc/README.md @@ -4,6 +4,7 @@ by reading the infrastructure code itself. For cryptography information, see the [CrossPlatformCryptography.md](CrossPlatformCryptography.md) doc. For dev scenario documentation, see the [DeveloperGuide.md](DeveloperGuide.md) doc. +For telemetry details, see the [Telemetry.md](Telemetry.md) doc. 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. diff --git a/eng/doc/Telemetry.md b/eng/doc/Telemetry.md new file mode 100644 index 0000000000..8c70e60f25 --- /dev/null +++ b/eng/doc/Telemetry.md @@ -0,0 +1,75 @@ +# 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 Go Telemetry](https://devblogs.microsoft.com/go/microsoft-go-telemetry/). + +## What is collected + +All telemetry data consists of **counters** — simple event names that are incremented. +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 collected are defined in the [telemetry upload configuration](/go/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json) and are listed below. + +### Invocation counter + +| Counter | Description | +|---|---| +| `go/invocations` | Incremented once per `go` command invocation. | + +### 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 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/patches/0001-Vendor-external-dependencies.patch b/patches/0001-Vendor-external-dependencies.patch index 7bcc747a11..d379ef6f3d 100644 --- a/patches/0001-Vendor-external-dependencies.patch +++ b/patches/0001-Vendor-external-dependencies.patch @@ -13,9 +13,10 @@ 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/config/config.json | 79 + .../go-infra/telemetry/counter/counter.go | 63 + .../telemetry/internal/appinsights/README.md | 12 + .../telemetry/internal/appinsights/client.go | 192 ++ @@ -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, 35730 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,30 +529,30 @@ 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 29b78693abd787..dbc87f66957dcb 100644 +index 29b78693abd787..ffcaa3d7848408 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 ++ github.com/microsoft/go-infra/telemetry v0.0.0-20260522114235-13e33af6063a ++ github.com/microsoft/go-infra/telemetry/config v0.0.0-20260522114235-13e33af6063a golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 golang.org/x/build v0.0.0-20260122183339-3ba88df37c64 golang.org/x/mod v0.36.1-0.20260513122029-343ee60345a1 diff --git a/src/cmd/go.sum b/src/cmd/go.sum -index 929e1c34dcbec1..b873d102f0a9f7 100644 +index 929e1c34dcbec1..68262698d63570 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= 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-20260522114235-13e33af6063a h1:1yIH/VJLOUUWw2yVBEqAqDsakqIsx1TyjHv3rQuVv2A= ++github.com/microsoft/go-infra/telemetry v0.0.0-20260522114235-13e33af6063a/go.mod h1:LxxLUDlqi1gwmGrnh1slAFqnEhZnKjn37MABZ6xJs44= ++github.com/microsoft/go-infra/telemetry/config v0.0.0-20260522114235-13e33af6063a h1:F/bJDa61Hj7ISaUMyErZ9m0GIgHSaLMrhf2f1Pja9Io= ++github.com/microsoft/go-infra/telemetry/config v0.0.0-20260522114235-13e33af6063a/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.20260521044007-9c1a596a2c97 h1:OEbDVxixMxnrAI3whhcFkCb0rPrEHwxeSSUMdN0V414= @@ -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..89ce84ff435575 --- /dev/null +++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json -@@ -0,0 +1,76 @@ +@@ -0,0 +1,79 @@ +{ + "GOOS": [ + "aix", @@ -730,16 +835,19 @@ 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}" ++ }, ++ { ++ "Name": "msgo/module:*" + } + ] + } @@ -2220,14 +2328,14 @@ index 00000000000000..e50deaa4189b18 + return false +} diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt -index f3ed9c985e7bf5..96de94b746e601 100644 +index f3ed9c985e7bf5..67e1651ba7e3c5 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-20260522114235-13e33af6063a +## explicit; go 1.25 +github.com/microsoft/go-infra/telemetry +github.com/microsoft/go-infra/telemetry/counter @@ -2235,7 +2343,7 @@ index f3ed9c985e7bf5..96de94b746e601 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-20260522114235-13e33af6063a +## explicit; go 1.24 +github.com/microsoft/go-infra/telemetry/config # golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 diff --git a/patches/0009-Add-appinsights-telemetry.patch b/patches/0009-Add-appinsights-telemetry.patch index 9bb8d64efb..53fc64f914 100644 --- a/patches/0009-Add-appinsights-telemetry.patch +++ b/patches/0009-Add-appinsights-telemetry.patch @@ -5,14 +5,14 @@ Subject: [PATCH] Add appinsights telemetry --- README.md | 15 +++++ - .../internal/telemetrystats/telemetrystats.go | 10 ++++ + .../internal/telemetrystats/telemetrystats.go | 17 ++++++ 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(-) + 8 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/cmd/go/mstelemetry_test.go diff --git a/README.md b/README.md @@ -40,22 +40,30 @@ 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..0cd7d3582c9ee6 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" ++ "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") @@ -68,7 +76,7 @@ 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..cb7801588662a2 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -110,6 +110,14 @@ func main() { From 873caac9c16b660903bbcb2939d95e721def9ef4 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 26 May 2026 14:42:30 +0200 Subject: [PATCH 10/36] use FIPSCapable --- .../0001-Vendor-external-dependencies.patch | 359 +++++++++--------- ...03-Implement-crypto-internal-backend.patch | 18 +- 2 files changed, 183 insertions(+), 194 deletions(-) diff --git a/patches/0001-Vendor-external-dependencies.patch b/patches/0001-Vendor-external-dependencies.patch index d379ef6f3d..390e56a14e 100644 --- a/patches/0001-Vendor-external-dependencies.patch +++ b/patches/0001-Vendor-external-dependencies.patch @@ -207,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 + @@ -271,7 +271,7 @@ 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 + - 263 files changed, 35730 insertions(+), 7 deletions(-) + 263 files changed, 35719 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 @@ -2378,7 +2378,7 @@ 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 8703fad3b226c9..92b03d8db89373 100644 --- a/src/go.mod +++ b/src/go.mod @@ -11,3 +11,9 @@ require ( @@ -2388,18 +2388,18 @@ index 8703fad3b226c9..fe3cd514ef512d 100644 + +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 43554ff45c183e..30e68847197201 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= @@ -26705,10 +26705,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. + @@ -26795,8 +26795,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 @@ -27881,10 +27880,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. + @@ -27980,7 +27979,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) @@ -27995,8 +27994,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{ @@ -28046,7 +28043,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) @@ -28057,8 +28054,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 +} @@ -28077,16 +28072,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 @@ -28132,8 +28123,6 @@ index 00000000000000..82b7b1081468de +} + +func newDSA3(params DSAParameters, x, y BigInt) (ossl.EVP_PKEY_PTR, error) { -+ checkMajorVersion(3, 4) -+ + bld := newParamBuilder() + defer bld.finalize() + @@ -28331,10 +28320,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. + @@ -28444,7 +28433,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 @@ -28456,8 +28445,6 @@ index 00000000000000..7289538d753dc8 + } + bytes = goBytes(unsafe.Pointer(cbytes), n) + cryptoFree(unsafe.Pointer(cbytes)) -+ default: -+ panic(errUnsupportedVersion()) + } + } + pub := &PublicKeyECDH{pkey, bytes} @@ -28478,16 +28465,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 @@ -28540,8 +28523,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) @@ -28650,13 +28631,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. @@ -28678,10 +28657,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. + @@ -28776,7 +28755,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 + } @@ -28787,8 +28766,6 @@ index 00000000000000..d96c368fe1ca76 + return nil, nil, nil, err + } + defer ossl.BN_clear_free(bd) -+ default: -+ panic(errUnsupportedVersion()) + } + + // Get D. @@ -28834,16 +28811,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 @@ -28866,8 +28839,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) @@ -28904,7 +28875,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 @@ @@ -28944,7 +28915,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) @@ -29119,10 +29090,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. + @@ -29284,8 +29255,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 @@ -29306,23 +29276,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) @@ -29358,7 +29332,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: @@ -29385,8 +29359,6 @@ index 00000000000000..65f4d2ddcbc6fb + if err != nil { + return nil, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + + return pkey, nil @@ -29511,10 +29483,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)) @@ -30267,10 +30239,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. + @@ -30289,11 +30261,9 @@ index 00000000000000..75207faf1aa043 + switch major() { + case 1: + return true -+ case 3, 4: ++ default: + _, err := fetchHKDF3() + return err == nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -30302,18 +30272,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 @@ -30402,7 +30368,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 @@ -30417,8 +30383,6 @@ index 00000000000000..75207faf1aa043 + return nil, err + } + return out, nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -30451,7 +30415,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 @@ -30466,8 +30430,6 @@ index 00000000000000..75207faf1aa043 + if _, err := ossl.EVP_KDF_derive(ctx, out, nil); err != nil { + return nil, err + } -+ default: -+ panic(errUnsupportedVersion()) + } + return out, nil +} @@ -30501,8 +30463,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 @@ -30512,8 +30472,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 @@ -30554,8 +30512,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 @@ -30565,8 +30521,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 @@ -30603,10 +30557,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. + @@ -30653,14 +30607,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 @@ -30778,12 +30730,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. @@ -30803,10 +30753,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) @@ -30837,15 +30785,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]...) +} @@ -30869,7 +30815,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) @@ -30882,9 +30828,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 @@ -31719,10 +31662,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. + @@ -31766,6 +31709,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) +} @@ -31774,16 +31726,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" } @@ -32169,10 +32111,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. + @@ -32191,11 +32133,9 @@ index 00000000000000..66af2060e2cf87 + switch major() { + case 1: + return true -+ case 3, 4: ++ default: + _, err := fetchPBKDF2() + return err == nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -32203,8 +32143,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 @@ -32261,10 +32199,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. + @@ -32462,7 +32400,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 + } +} + @@ -32979,10 +32924,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. + @@ -33023,7 +32968,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) @@ -33052,8 +32997,6 @@ index 00000000000000..36f84902a21ece + setBigInt(&Qinv, _OSSL_PKEY_PARAM_RSA_COEFFICIENT1)) { + return bad(err) + } -+ default: -+ panic(errUnsupportedVersion()) + } + return +} @@ -33091,13 +33034,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) @@ -33170,13 +33111,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) @@ -33697,10 +33636,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. + @@ -33720,11 +33659,9 @@ index 00000000000000..87fe3879ab48d3 + switch major() { + case 1: + return minor() >= 1 -+ case 3, 4: ++ default: + _, err := fetchTLS1PRF3() + return err == nil -+ default: -+ panic(errUnsupportedVersion()) + } +} + @@ -33754,17 +33691,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 @@ -33818,8 +33751,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 @@ -33829,8 +33760,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 @@ -33953,10 +33882,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. + @@ -33991,20 +33920,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) @@ -34014,6 +33982,7 @@ index 00000000000000..2084b384b9f3a9 + provDefault := sha256Provider("") + return provFIPS == provDefault + } ++ return false +} + +// sha256Provider returns the provider for the SHA-256 algorithm @@ -34026,6 +33995,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 @@ -34328,10 +34303,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. + @@ -34357,6 +34332,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. @@ -39941,7 +39930,7 @@ index 00000000000000..1722410e5af193 + return getSystemDirectory() + "\\" + dll +} diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt -index d72017d2a43c1e..277d1992ac034f 100644 +index d72017d2a43c1e..a81d4929faa013 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -1,3 +1,26 @@ @@ -39954,7 +39943,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 diff --git a/patches/0003-Implement-crypto-internal-backend.patch b/patches/0003-Implement-crypto-internal-backend.patch index c353312ea7..1513a00311 100644 --- a/patches/0003-Implement-crypto-internal-backend.patch +++ b/patches/0003-Implement-crypto-internal-backend.patch @@ -146,10 +146,10 @@ index 0e32e0769ee2e2..eda2d576acc6c6 100644 if doReplacement { if testCompiler == "" { diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go -index de2c7be2e833f9..415f4baeff2510 100644 +index 78d55bec559987..bb3119ef0823d7 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go -@@ -1398,6 +1398,80 @@ func toolenv() []string { +@@ -1399,6 +1399,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") } @@ -230,7 +230,7 @@ index de2c7be2e833f9..415f4baeff2510 100644 return env } -@@ -1640,12 +1714,12 @@ func cmdbootstrap() { +@@ -1641,12 +1715,12 @@ func cmdbootstrap() { os.Setenv("CC", compilerEnvLookup("CC", defaultcc, goos, goarch)) xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch) } @@ -336,7 +336,7 @@ index 47839e0229b951..2d1a7fa99d551e 100644 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 +index 78cbbb949e7c27..dcf9d04dd979f3 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -19,6 +19,7 @@ import ( @@ -674,7 +674,7 @@ index 2f46a6b44bffe1..69071289e2de81 100644 ! 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 +index d81b3a849cb975..bbeca248e4f8e4 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) { @@ -1187,7 +1187,7 @@ index 00000000000000..73830cae246cbd +} 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 +index 00000000000000..d8073b4f6c282e --- /dev/null +++ b/src/crypto/internal/backend/backend_linux.go @@ -0,0 +1,435 @@ @@ -1226,8 +1226,8 @@ index 00000000000000..a28f644c950f86 + // 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 { ++ // osslsetup.FIPSCapable to determine whether FIPS mode is enabled. ++ if err := fips140.Check(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. @@ -3308,7 +3308,7 @@ index 00000000000000..7500bd3a86472b + ` +} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index 504426b5fe97f2..a8aee3a4bccda3 100644 +index 920e72e5cdbb9d..2135c64d86664d 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -368,7 +368,7 @@ var depsRules = ` From c97ab253ffdff7efb38f732a0b07732406b0bd70 Mon Sep 17 00:00:00 2001 From: George Adams Date: Wed, 27 May 2026 10:42:07 +0100 Subject: [PATCH 11/36] Update blog post link description in Telemetry.md (#2316) fixes: https://github.com/microsoft/go-lab/issues/406 --- eng/doc/Telemetry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/doc/Telemetry.md b/eng/doc/Telemetry.md index 8c70e60f25..70177515f0 100644 --- a/eng/doc/Telemetry.md +++ b/eng/doc/Telemetry.md @@ -4,7 +4,7 @@ The Microsoft build of Go collects opt-out (enabled by default) toolset telemetr 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 Go Telemetry](https://devblogs.microsoft.com/go/microsoft-go-telemetry/). +For the blog post announcing this feature, see [Microsoft build of Go Telemetry](https://devblogs.microsoft.com/go/microsoft-go-telemetry/). ## What is collected From 61166d1aa811d31d282a128c959f01a882906647 Mon Sep 17 00:00:00 2001 From: "bot-for-go[bot]" <199222863+bot-for-go[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 16:07:50 +0000 Subject: [PATCH 12/36] Update submodule to latest master (286c12b1): debug/elf: add FuzzReader --- go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go b/go index 9da73fb702..286c12b1d1 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit 9da73fb7025cd64b71608241517d6cb17735d854 +Subproject commit 286c12b1d159de830564271b3589c8a7b8acbca7 From 6e93cf6858d5741210b2c096f9ff2e2cf306fca7 Mon Sep 17 00:00:00 2001 From: George Adams Date: Thu, 28 May 2026 12:37:40 +0100 Subject: [PATCH 13/36] resolve conflicts --- .../0001-Vendor-external-dependencies.patch | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/patches/0001-Vendor-external-dependencies.patch b/patches/0001-Vendor-external-dependencies.patch index 390e56a14e..18868d71dd 100644 --- a/patches/0001-Vendor-external-dependencies.patch +++ b/patches/0001-Vendor-external-dependencies.patch @@ -529,24 +529,24 @@ 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 29b78693abd787..ffcaa3d7848408 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/google/pprof v0.0.0-20260507013755-92041b743c96 + github.com/microsoft/go-infra/telemetry v0.0.0-20260522114235-13e33af6063a + github.com/microsoft/go-infra/telemetry/config v0.0.0-20260522114235-13e33af6063a golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 - golang.org/x/build v0.0.0-20260122183339-3ba88df37c64 - golang.org/x/mod v0.36.1-0.20260513122029-343ee60345a1 + 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 929e1c34dcbec1..68262698d63570 100644 +index d9e3e3992ebb61..792375dd36e664 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-20260522114235-13e33af6063a h1:1yIH/VJLOUUWw2yVBEqAqDsakqIsx1TyjHv3rQuVv2A= @@ -2328,7 +2328,7 @@ index 00000000000000..e50deaa4189b18 + return false +} diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt -index f3ed9c985e7bf5..67e1651ba7e3c5 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 @@ -2378,11 +2378,11 @@ 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..92b03d8db89373 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 ) + @@ -2392,7 +2392,7 @@ index 8703fad3b226c9..92b03d8db89373 100644 + github.com/microsoft/go-crypto-winnative v0.0.0-20260512074019-00d811a4aefe +) diff --git a/src/go.sum b/src/go.sum -index 43554ff45c183e..30e68847197201 100644 +index ab34844da17757..060e1a1e1ae6fc 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,3 +1,9 @@ @@ -2402,9 +2402,9 @@ index 43554ff45c183e..30e68847197201 100644 +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..920e72e5cdbb9d 100644 --- a/src/go/build/deps_test.go @@ -39930,7 +39930,7 @@ index 00000000000000..1722410e5af193 + return getSystemDirectory() + "\\" + dll +} diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt -index d72017d2a43c1e..a81d4929faa013 100644 +index 54fcbab6a221c0..07fcd41c1e259f 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -1,3 +1,26 @@ @@ -39957,6 +39957,6 @@ index d72017d2a43c1e..a81d4929faa013 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 From dfdb941a7b25ad21623b975239424c9ec37b203c Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 28 May 2026 09:57:54 -0700 Subject: [PATCH 14/36] Update project documentation structure and clarity (#2318) * Update project documentation structure and clarity * Explicitly label eng.ms internal links (suggestion) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- README.md | 41 +++++++++++++++++++++-------------------- SUPPORT.md | 3 +++ eng/doc/README.md | 29 +++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 808d23a4eb..a8226310f2 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 5594fc5c2f..a5c8aaa4b5 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/doc/README.md b/eng/doc/README.md index c7641610b3..d974d1daa6 100644 --- a/eng/doc/README.md +++ b/eng/doc/README.md @@ -1,10 +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. -For telemetry details, see the [Telemetry.md](Telemetry.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) From 835632721472b868c07ce7a286460ced3cbc7506 Mon Sep 17 00:00:00 2001 From: George Adams Date: Thu, 28 May 2026 22:20:29 +0100 Subject: [PATCH 15/36] Add msgo/module/hash telemetry counter (#2283) * update * review fixes --- eng/doc/Telemetry.md | 11 +- .../0001-Vendor-external-dependencies.patch | 118 ++++++++++++------ patches/0009-Add-appinsights-telemetry.patch | 84 ++++++++++--- 3 files changed, 156 insertions(+), 57 deletions(-) diff --git a/eng/doc/Telemetry.md b/eng/doc/Telemetry.md index 70177515f0..3877a6dd6a 100644 --- a/eng/doc/Telemetry.md +++ b/eng/doc/Telemetry.md @@ -8,11 +8,11 @@ For the blog post announcing this feature, see [Microsoft build of Go Telemetry] ## What is collected -All telemetry data consists of **counters** — simple event names that are incremented. +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 collected are defined in the [telemetry upload configuration](/go/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json) and are listed below. +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 @@ -20,6 +20,12 @@ The counters collected are defined in the [telemetry upload configuration](/go/s |---|---| | `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 | @@ -55,6 +61,7 @@ The counters collected are defined in the [telemetry upload configuration](/go/s - 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). diff --git a/patches/0001-Vendor-external-dependencies.patch b/patches/0001-Vendor-external-dependencies.patch index 18868d71dd..58d2cf993e 100644 --- a/patches/0001-Vendor-external-dependencies.patch +++ b/patches/0001-Vendor-external-dependencies.patch @@ -16,10 +16,10 @@ Use a 'go' that was recently built by the current branch to ensure stable result .../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 | 79 + - .../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 + @@ -28,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 + @@ -271,7 +271,7 @@ 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 + - 263 files changed, 35719 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 @@ -536,23 +536,23 @@ index 82ceadb04a273a..b7fe75a52e69f8 100644 require ( github.com/google/pprof v0.0.0-20260507013755-92041b743c96 -+ github.com/microsoft/go-infra/telemetry v0.0.0-20260522114235-13e33af6063a -+ github.com/microsoft/go-infra/telemetry/config v0.0.0-20260522114235-13e33af6063a ++ 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 d9e3e3992ebb61..792375dd36e664 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-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-20260522114235-13e33af6063a h1:1yIH/VJLOUUWw2yVBEqAqDsakqIsx1TyjHv3rQuVv2A= -+github.com/microsoft/go-infra/telemetry v0.0.0-20260522114235-13e33af6063a/go.mod h1:LxxLUDlqi1gwmGrnh1slAFqnEhZnKjn37MABZ6xJs44= -+github.com/microsoft/go-infra/telemetry/config v0.0.0-20260522114235-13e33af6063a h1:F/bJDa61Hj7ISaUMyErZ9m0GIgHSaLMrhf2f1Pja9Io= -+github.com/microsoft/go-infra/telemetry/config v0.0.0-20260522114235-13e33af6063a/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.20260521044007-9c1a596a2c97 h1:OEbDVxixMxnrAI3whhcFkCb0rPrEHwxeSSUMdN0V414= @@ -770,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..89ce84ff435575 +index 00000000000000..e2ba3596943583 --- /dev/null +++ b/src/cmd/vendor/github.com/microsoft/go-infra/telemetry/config/config.json -@@ -0,0 +1,79 @@ +@@ -0,0 +1,77 @@ +{ + "GOOS": [ + "aix", @@ -826,7 +826,8 @@ index 00000000000000..89ce84ff435575 + "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}" @@ -845,9 +846,6 @@ index 00000000000000..89ce84ff435575 + }, + { + "Name": "msgo/systemcrypto:{enabled,disabled}" -+ }, -+ { -+ "Name": "msgo/module:*" + } + ] + } @@ -856,10 +854,10 @@ index 00000000000000..89ce84ff435575 \ 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. + @@ -892,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. @@ -943,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. + @@ -1002,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 + @@ -1103,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 @@ -2005,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. + @@ -2029,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) { @@ -2179,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. + @@ -2239,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) { @@ -2276,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 == "" { @@ -2288,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 ++ } + } + } + } @@ -2302,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, + }) +} + @@ -2317,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 { @@ -2327,6 +2353,20 @@ 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 4774f1a02e905f..3c77716f1fe9b0 100644 --- a/src/cmd/vendor/modules.txt @@ -2335,7 +2375,7 @@ index 4774f1a02e905f..3c77716f1fe9b0 100644 # 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-20260522114235-13e33af6063a ++# 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 @@ -2343,7 +2383,7 @@ index 4774f1a02e905f..3c77716f1fe9b0 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-20260522114235-13e33af6063a ++# 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.20260521044007-9c1a596a2c97 diff --git a/patches/0009-Add-appinsights-telemetry.patch b/patches/0009-Add-appinsights-telemetry.patch index 53fc64f914..a5118539d8 100644 --- a/patches/0009-Add-appinsights-telemetry.patch +++ b/patches/0009-Add-appinsights-telemetry.patch @@ -6,13 +6,13 @@ Subject: [PATCH] Add appinsights telemetry --- README.md | 15 +++++ .../internal/telemetrystats/telemetrystats.go | 17 ++++++ - 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, 184 insertions(+), 1 deletion(-) + 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 @@ -76,13 +76,33 @@ index c6e58935bb0ee2..0cd7d3582c9ee6 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 bba11a2fdeaaa6..cb7801588662a2 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": @@ -91,9 +111,24 @@ index bba11a2fdeaaa6..cb7801588662a2 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 @@ -232,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 @@ @@ -260,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")) } @@ -302,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) @@ -313,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 { @@ -325,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) {} From 54c8cea18764985d2feedfb70450fa267144207e Mon Sep 17 00:00:00 2001 From: "bot-for-go[bot]" <199222863+bot-for-go[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 16:07:43 +0000 Subject: [PATCH 16/36] Update submodule to latest master (7e3a04fb): doc/next: delete --- go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go b/go index 286c12b1d1..7e3a04fb91 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit 286c12b1d159de830564271b3589c8a7b8acbca7 +Subproject commit 7e3a04fb9140963ce1c60bd3fda6f83833ca8b7d From 32418d6a3256e02c7396698b9afb8de959432fc7 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 29 May 2026 13:37:59 +0200 Subject: [PATCH 17/36] Use env flag for nosystemcrypto builders --- eng/pipeline/stages/builders-to-stages.yml | 2 +- .../stages/go-builder-matrix-stages.yml | 86 +++++++++---------- eng/pipeline/stages/run-codeql.yml | 6 +- eng/pipeline/stages/run-stage.yml | 14 ++- .../stages/shorthand-builders-to-builders.yml | 8 +- eng/pipeline/stages/sign-stage.yml | 2 +- 6 files changed, 64 insertions(+), 54 deletions(-) diff --git a/eng/pipeline/stages/builders-to-stages.yml b/eng/pipeline/stages/builders-to-stages.yml index 98de2fedb8..fc45f32077 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 b556c35f04..9e82b76677 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 f29e84b46c..b302e4195b 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 cbf66ed390..fe2455c037 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 50ddb375a4..10357ece7e 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 77191b2407..45d260bb8e 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 From 474bbff88f77d4a7276a538232c59be3563d743c Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Fri, 29 May 2026 13:44:06 +0200 Subject: [PATCH 18/36] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- eng/pipeline/stages/builders-to-stages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipeline/stages/builders-to-stages.yml b/eng/pipeline/stages/builders-to-stages.yml index fc45f32077..28563e6715 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?, nosystemcrypto?, fips?, 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. From 39ff325fe67e090e88692d5733b47e58855f0abf Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 1 Jun 2026 14:29:09 +0200 Subject: [PATCH 19/36] fix cmd/dist --- ...002-Add-crypto-backend-GOEXPERIMENTs.patch | 110 +++++++++++++++++- ...03-Implement-crypto-internal-backend.patch | 60 +++++++--- patches/0004-Use-crypto-backends.patch | 29 ++--- 3 files changed, 169 insertions(+), 30 deletions(-) diff --git a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch index d8a2206551..eb7a94f009 100644 --- a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch +++ b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch @@ -17,6 +17,7 @@ maintain this feature. For more information, see the test files. 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 +++++++++++++ + src/go/build/deps_test.go | 2 + .../build/testdata/backendtags_system/main.go | 3 + .../backendtags_system/systemcrypto.go | 3 + src/internal/buildcfg/cfg.go | 2 + @@ -32,7 +33,9 @@ maintain this feature. For more information, see the test files. .../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(-) + src/internal/systemcrypto/systemcrypto.go | 20 +++++ + .../systemcrypto/systemcrypto_test.go | 58 +++++++++++++++ + 24 files changed, 351 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 @@ -45,6 +48,8 @@ maintain this feature. For more information, see the test files. 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 + create mode 100644 src/internal/systemcrypto/systemcrypto.go + create mode 100644 src/internal/systemcrypto/systemcrypto_test.go diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 74cafe9613917d..31ce076273aba2 100644 @@ -258,6 +263,19 @@ index 00000000000000..ffb835ce34a2f7 + 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 920e72e5cdbb9d..7834d307b0426d 100644 +--- a/src/go/build/deps_test.go ++++ b/src/go/build/deps_test.go +@@ -370,6 +370,8 @@ var depsRules = ` + + FMT, internal/goexperiment + < 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 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 @@ -541,3 +559,93 @@ index 6f37e368596498..471c5cc430bb29 100644 + 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/patches/0003-Implement-crypto-internal-backend.patch b/patches/0003-Implement-crypto-internal-backend.patch index 1513a00311..47129c531a 100644 --- a/patches/0003-Implement-crypto-internal-backend.patch +++ b/patches/0003-Implement-crypto-internal-backend.patch @@ -13,7 +13,7 @@ desired goexperiments and build tags. .../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/dist/test.go | 27 +- src/cmd/go/go_test.go | 11 + src/cmd/go/internal/cfg/cfg.go | 16 + src/cmd/go/internal/load/pkg.go | 3 + @@ -54,7 +54,7 @@ desired goexperiments and build tags. 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(-) + 45 files changed, 2861 insertions(+), 29 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 @@ -247,10 +247,18 @@ index 78d55bec559987..bb3119ef0823d7 100644 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 +index aadb1ff52b067f..2ef0d4219b57d1 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go -@@ -158,10 +158,12 @@ func (t *tester) run() { +@@ -9,6 +9,7 @@ import ( + "encoding/json" + "flag" + "fmt" ++ "internal/systemcrypto" + "io" + "io/fs" + "log" +@@ -158,10 +159,12 @@ func (t *tester) run() { } } @@ -264,7 +272,7 @@ index aadb1ff52b067f..dab28b6f1ced0a 100644 } if !t.listMode { -@@ -178,9 +180,9 @@ func (t *tester) run() { +@@ -178,9 +181,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. @@ -277,28 +285,48 @@ index aadb1ff52b067f..dab28b6f1ced0a 100644 } } -@@ -774,7 +776,9 @@ func (t *tester) registerTests() { +@@ -774,7 +777,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") { ++ !systemCryptoEnabled() { t.registerTest("GOOS=ios on darwin/amd64", &goTest{ variant: "amd64ios", -@@ -1023,7 +1027,9 @@ func (t *tester) registerTests() { +@@ -1023,7 +1028,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") { ++ 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,9 +1387,9 @@ 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 ++ return "skipping static linking check on Linux when using systemcrypto to avoid C linker warning about getaddrinfo", true + } + } + +@@ -1908,3 +1915,7 @@ func goexperiments(exps ...string) string { + return existing + strings.Join(exps, ",") + + } ++ ++func systemCryptoEnabled() bool { ++ return systemcrypto.EnabledFor(goos, goarch) ++} 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 @@ -384,7 +412,7 @@ index 1d90061b065f5f..0d5d4fe0556564 100644 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 +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 @@ -3308,7 +3336,7 @@ index 00000000000000..7500bd3a86472b + ` +} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index 920e72e5cdbb9d..2135c64d86664d 100644 +index 7834d307b0426d..148fff74886a58 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -368,7 +368,7 @@ var depsRules = ` @@ -3318,9 +3346,9 @@ index 920e72e5cdbb9d..2135c64d86664d 100644 - 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 = ` + internal/buildcfg + < internal/systemcrypto; +@@ -555,6 +555,10 @@ var depsRules = ` < github.com/microsoft/go-crypto-winnative/internal/sysdll < github.com/microsoft/go-crypto-winnative/internal/bcrypt; @@ -3331,7 +3359,7 @@ index 920e72e5cdbb9d..2135c64d86664d 100644 FIPS, internal/godebug, embed < crypto/internal/fips140only < crypto -@@ -573,6 +577,13 @@ var depsRules = ` +@@ -575,6 +579,13 @@ var depsRules = ` github.com/microsoft/go-crypto-winnative/internal/bcrypt < github.com/microsoft/go-crypto-winnative/cng; @@ -3345,7 +3373,7 @@ index 920e72e5cdbb9d..2135c64d86664d 100644 FIPS, internal/godebug, embed, crypto/internal/boring/sig, crypto/internal/boring/syso, -@@ -612,8 +623,15 @@ var depsRules = ` +@@ -614,8 +625,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; diff --git a/patches/0004-Use-crypto-backends.patch b/patches/0004-Use-crypto-backends.patch index 131d7d110a..2e3291f8d7 100644 --- a/patches/0004-Use-crypto-backends.patch +++ b/patches/0004-Use-crypto-backends.patch @@ -128,10 +128,10 @@ index f0e3575637c62a..9eab3b4e66e60b 100644 package main diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go -index dab28b6f1ced0a..3263a1700e5980 100644 +index 2ef0d4219b57d1..6d0657b3e5afa0 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go -@@ -722,7 +722,7 @@ func (t *tester) registerTests() { +@@ -723,7 +723,7 @@ func (t *tester) registerTests() { }) // Check that all crypto packages compile (and test correctly, in longmode) with fips. @@ -140,14 +140,17 @@ index dab28b6f1ced0a..3263a1700e5980 100644 // 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) { +@@ -1383,7 +1383,7 @@ func (t *tester) registerCgoTests(heading string) { + } + } + +- // Doing a static link with boringcrypto gets ++ // Doing a static link with systemcrypto gets // 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 +@@ -1392,7 +1392,6 @@ func (t *tester) registerCgoTests(heading string) { + return "skipping static linking check on Linux when using systemcrypto to avoid C linker warning about getaddrinfo", true } } - @@ -200,7 +203,7 @@ index fa445925b7c374..e4b9714df817ea 100644 ! 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 +index 4aaf46b5d0f0dc..995d53d0fed9aa 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 @@ @@ -4349,10 +4352,10 @@ index 1d3e845d0f0a9c..eb4318faf13f7b 100644 var dsaPriv dsa.PrivateKey diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index 7794e12bc86890..d6ef0bf10ebc1c 100644 +index 148fff74886a58..5d3f4a9850e259 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go -@@ -533,7 +533,7 @@ var depsRules = ` +@@ -535,7 +535,7 @@ var depsRules = ` < crypto/internal/fips140/edwards25519 < crypto/internal/fips140/ed25519 < crypto/internal/fips140/rsa @@ -4361,7 +4364,7 @@ index 7794e12bc86890..d6ef0bf10ebc1c 100644 crypto !< FIPS; -@@ -557,7 +557,12 @@ var depsRules = ` +@@ -559,7 +559,12 @@ var depsRules = ` < crypto/internal/backend/internal/opensslsetup < crypto/internal/backend/fips140; @@ -4375,7 +4378,7 @@ index 7794e12bc86890..d6ef0bf10ebc1c 100644 < crypto/internal/fips140only < crypto < crypto/subtle -@@ -591,7 +596,8 @@ var depsRules = ` +@@ -593,7 +598,8 @@ var depsRules = ` crypto/internal/fips140only, crypto, crypto/subtle, @@ -4385,7 +4388,7 @@ index 7794e12bc86890..d6ef0bf10ebc1c 100644 < crypto/sha3 < crypto/internal/fips140hash < crypto/internal/boring -@@ -610,6 +616,7 @@ var depsRules = ` +@@ -612,6 +618,7 @@ var depsRules = ` crypto/ecdh, crypto/mlkem, crypto/mldsa From 478616b2deef89840c7637d24ed960670b662f76 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 1 Jun 2026 14:44:45 +0200 Subject: [PATCH 20/36] Make cmd/dist systemcrypto check bootstrap-safe --- ...03-Implement-crypto-internal-backend.patch | 49 ++++++++++++------- patches/0004-Use-crypto-backends.patch | 10 ++-- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/patches/0003-Implement-crypto-internal-backend.patch b/patches/0003-Implement-crypto-internal-backend.patch index 47129c531a..01752c9fd0 100644 --- a/patches/0003-Implement-crypto-internal-backend.patch +++ b/patches/0003-Implement-crypto-internal-backend.patch @@ -13,7 +13,7 @@ desired goexperiments and build tags. .../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 | 27 +- + src/cmd/dist/test.go | 47 +- src/cmd/go/go_test.go | 11 + src/cmd/go/internal/cfg/cfg.go | 16 + src/cmd/go/internal/load/pkg.go | 3 + @@ -54,7 +54,7 @@ desired goexperiments and build tags. src/go/build/deps_test.go | 24 +- src/internal/buildcfg/exp.go | 48 +- src/runtime/runtime_boring.go | 5 + - 45 files changed, 2861 insertions(+), 29 deletions(-) + 45 files changed, 2881 insertions(+), 29 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 @@ -247,18 +247,10 @@ index 78d55bec559987..bb3119ef0823d7 100644 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..2ef0d4219b57d1 100644 +index aadb1ff52b067f..53d661338f2d72 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go -@@ -9,6 +9,7 @@ import ( - "encoding/json" - "flag" - "fmt" -+ "internal/systemcrypto" - "io" - "io/fs" - "log" -@@ -158,10 +159,12 @@ func (t *tester) run() { +@@ -158,10 +158,12 @@ func (t *tester) run() { } } @@ -272,7 +264,7 @@ index aadb1ff52b067f..2ef0d4219b57d1 100644 } if !t.listMode { -@@ -178,9 +181,9 @@ func (t *tester) run() { +@@ -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. @@ -285,7 +277,7 @@ index aadb1ff52b067f..2ef0d4219b57d1 100644 } } -@@ -774,7 +777,9 @@ func (t *tester) registerTests() { +@@ -774,7 +776,9 @@ func (t *tester) registerTests() { } // Test ios/amd64 for the iOS simulator. @@ -296,7 +288,7 @@ index aadb1ff52b067f..2ef0d4219b57d1 100644 t.registerTest("GOOS=ios on darwin/amd64", &goTest{ variant: "amd64ios", -@@ -1023,7 +1028,9 @@ func (t *tester) registerTests() { +@@ -1023,7 +1027,9 @@ func (t *tester) registerTests() { t.registerRaceTests() } @@ -307,7 +299,7 @@ index aadb1ff52b067f..2ef0d4219b57d1 100644 // Only start multiple test dir shards on builders, // where they get distributed to multiple machines. // See issues 20141 and 31834. -@@ -1380,9 +1387,9 @@ func (t *tester) registerCgoTests(heading string) { +@@ -1380,9 +1386,9 @@ 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 @@ -319,13 +311,34 @@ index aadb1ff52b067f..2ef0d4219b57d1 100644 } } -@@ -1908,3 +1915,7 @@ func goexperiments(exps ...string) string { +@@ -1908,3 +1914,28 @@ func goexperiments(exps ...string) string { return existing + strings.Join(exps, ",") } + +func systemCryptoEnabled() bool { -+ return systemcrypto.EnabledFor(goos, goarch) ++ // 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/go_test.go b/src/cmd/go/go_test.go index 47839e0229b951..2d1a7fa99d551e 100644 diff --git a/patches/0004-Use-crypto-backends.patch b/patches/0004-Use-crypto-backends.patch index 2e3291f8d7..58765e91b1 100644 --- a/patches/0004-Use-crypto-backends.patch +++ b/patches/0004-Use-crypto-backends.patch @@ -128,10 +128,10 @@ index f0e3575637c62a..9eab3b4e66e60b 100644 package main diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go -index 2ef0d4219b57d1..6d0657b3e5afa0 100644 +index 53d661338f2d72..f87b5a6c5fd7fe 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go -@@ -723,7 +723,7 @@ func (t *tester) registerTests() { +@@ -722,7 +722,7 @@ func (t *tester) registerTests() { }) // Check that all crypto packages compile (and test correctly, in longmode) with fips. @@ -140,7 +140,7 @@ index 2ef0d4219b57d1..6d0657b3e5afa0 100644 // Test standard crypto packages with fips140=on. t.registerTest("GOFIPS140=latest go test crypto/...", &goTest{ variant: "gofips140", -@@ -1383,7 +1383,7 @@ func (t *tester) registerCgoTests(heading string) { +@@ -1382,7 +1382,7 @@ func (t *tester) registerCgoTests(heading string) { } } @@ -149,7 +149,7 @@ index 2ef0d4219b57d1..6d0657b3e5afa0 100644 // 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 -@@ -1392,7 +1392,6 @@ func (t *tester) registerCgoTests(heading string) { +@@ -1391,7 +1391,6 @@ func (t *tester) registerCgoTests(heading string) { return "skipping static linking check on Linux when using systemcrypto to avoid C linker warning about getaddrinfo", true } } @@ -1905,7 +1905,7 @@ index 7f2824ca9ac052..f0d3b2a8459871 100644 "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 +index d12cc7586c45cc..c314c08c3186fb 100644 --- a/src/crypto/internal/rand/rand.go +++ b/src/crypto/internal/rand/rand.go @@ -5,7 +5,7 @@ From 4a274e6ae2829334210552f604a6f2000d013401 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 29 May 2026 16:27:07 +0200 Subject: [PATCH 21/36] remove GOEXPERIMENT=systemcrypto support --- ...002-Add-crypto-backend-GOEXPERIMENTs.patch | 371 ++++++------- ...03-Implement-crypto-internal-backend.patch | 486 ++++++++---------- patches/0004-Use-crypto-backends.patch | 91 ++-- patches/0009-Add-appinsights-telemetry.patch | 10 +- ...13-Add-Microsoft-version-information.patch | 28 +- 5 files changed, 425 insertions(+), 561 deletions(-) diff --git a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch index eb7a94f009..26b349c8ac 100644 --- a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch +++ b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch @@ -12,8 +12,10 @@ 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/cfg/cfg.go | 6 +- 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/testdata/script/env_changed.txt | 3 + src/go/build/buildbackend_test.go | 50 +++++++++++++ @@ -21,33 +23,17 @@ maintain this feature. For more information, see the test files. .../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/buildcfg/cfg_test.go | 44 +++++++++++ + src/internal/buildcfg/exp.go | 20 +++++ 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 +++ src/internal/systemcrypto/systemcrypto.go | 20 +++++ .../systemcrypto/systemcrypto_test.go | 58 +++++++++++++++ - 24 files changed, 351 insertions(+), 1 deletion(-) + 18 files changed, 308 insertions(+), 2 deletions(-) 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 create mode 100644 src/internal/systemcrypto/systemcrypto.go create mode 100644 src/internal/systemcrypto/systemcrypto_test.go @@ -65,6 +51,33 @@ index 74cafe9613917d..31ce076273aba2 100644 // // Additional information available from 'go env' but not read from the environment: // +diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go +index 78cbbb949e7c27..9bdbfb29c2752b 100644 +--- a/src/cmd/go/internal/cfg/cfg.go ++++ b/src/cmd/go/internal/cfg/cfg.go +@@ -14,6 +14,7 @@ import ( + "internal/buildcfg" + "internal/cfg" + "internal/platform" ++ "internal/systemcrypto" + "io" + "io/fs" + "os" +@@ -311,10 +312,13 @@ 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)+1) + for _, exp := range exps { + expTags = append(expTags, "goexperiment."+exp) + } ++ if systemcrypto.EnabledFor(Goos, Goarch) { ++ expTags = append(expTags, "goexperiment.systemcrypto") ++ } + 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 @@ -100,6 +113,28 @@ index 4f76f7f9c1fc68..783d11609c0bf4 100644 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 1d90061b065f5f..ba582cf1d0773e 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" +@@ -2417,6 +2418,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 @@ -307,77 +342,110 @@ index 89fd74eb823162..6d266ff641d721 100644 // 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 +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 ( - "fmt" -+ "os" - "reflect" - "strings" + "os" ++ "slices" ++ "strings" + "testing" + ) -@@ -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 += "," +@@ -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) + } -+ 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 ++ 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 + - // 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 + } - 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 +@@ -131,6 +133,21 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { if strings.HasPrefix(f, "no") { -@@ -152,6 +183,9 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { + 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") } @@ -398,147 +466,6 @@ index 9329769721b7de..1724afb71b851e 100644 + 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 diff --git a/patches/0003-Implement-crypto-internal-backend.patch b/patches/0003-Implement-crypto-internal-backend.patch index 01752c9fd0..b3f65900f9 100644 --- a/patches/0003-Implement-crypto-internal-backend.patch +++ b/patches/0003-Implement-crypto-internal-backend.patch @@ -12,17 +12,19 @@ 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 | 47 +- + src/cmd/dist/build.go | 68 ++- + src/cmd/dist/test.go | 45 +- 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 +++++++++ + .../verylongtest/testdata/script/README | 2 + + src/cmd/go/systemcrypto_test.go | 250 ++++++++++ + src/cmd/go/testdata/script/README | 2 + + src/cmd/go/testdata/script/darwin_no_cgo.txt | 1 + .../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 +- + .../internal/script/scripttest/conditions.go | 10 +- + src/cmd/internal/testdir/testdir_test.go | 6 + src/cmd/link/link_test.go | 8 + src/crypto/internal/backend/backend_darwin.go | 442 ++++++++++++++++++ src/crypto/internal/backend/backend_linux.go | 435 +++++++++++++++++ @@ -52,9 +54,8 @@ desired goexperiments and build tags. .../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, 2881 insertions(+), 29 deletions(-) + 46 files changed, 2857 insertions(+), 17 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 @@ -97,14 +98,14 @@ index 344b31f7ac1c1d..b4583113d5aa15 100644 # 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 +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/goexperiment" ++ "internal/systemcrypto" "internal/testenv" "os" "path/filepath" @@ -112,7 +113,7 @@ index 1edabf9fb7ff04..f47c17401e480b 100644 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 { ++ if systemcrypto.Enabled() { + // SystemCrypto does not support cross-compilation on all architectures. + arches = []string{"arm", "arm64", "386", "amd64", "loong64", "ppc64le", "riscv64", "s390x"} + } @@ -120,14 +121,14 @@ index 1edabf9fb7ff04..f47c17401e480b 100644 } diff --git a/src/cmd/compile/script_test.go b/src/cmd/compile/script_test.go -index 0e32e0769ee2e2..eda2d576acc6c6 100644 +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/goexperiment" ++ "internal/systemcrypto" "internal/testenv" "os" "runtime" @@ -135,7 +136,7 @@ index 0e32e0769ee2e2..eda2d576acc6c6 100644 // the installed linker with our test binary. doReplacement = false } -+ if goexperiment.SystemCrypto { ++ 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 @@ -146,10 +147,10 @@ index 0e32e0769ee2e2..eda2d576acc6c6 100644 if doReplacement { if testCompiler == "" { diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go -index 78d55bec559987..bb3119ef0823d7 100644 +index 78d55bec559987..decf356a449ba9 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go -@@ -1399,6 +1399,80 @@ func toolenv() []string { +@@ -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") } @@ -185,27 +186,15 @@ index 78d55bec559987..bb3119ef0823d7 100644 + +// 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, ",") ++ // 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 not built with the systemcrypto goexperiment to be used -+ // when GODEBUG=fips140=on is set. For example, when running ++ // 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 == "" { @@ -230,7 +219,7 @@ index 78d55bec559987..bb3119ef0823d7 100644 return env } -@@ -1641,12 +1715,12 @@ func cmdbootstrap() { +@@ -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) } @@ -247,7 +236,7 @@ index 78d55bec559987..bb3119ef0823d7 100644 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..53d661338f2d72 100644 +index aadb1ff52b067f..dc6981715e9ce1 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -158,10 +158,12 @@ func (t *tester) run() { @@ -299,18 +288,15 @@ index aadb1ff52b067f..53d661338f2d72 100644 // Only start multiple test dir shards on builders, // where they get distributed to multiple machines. // See issues 20141 and 31834. -@@ -1380,9 +1386,9 @@ func (t *tester) registerCgoTests(heading string) { +@@ -1380,7 +1386,7 @@ 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 -+ return "skipping static linking check on Linux when using systemcrypto to avoid C linker warning about getaddrinfo", true + return "skipping static linking check on Linux when using boringcrypto to avoid C linker warning about getaddrinfo", true } - } - @@ -1908,3 +1914,28 @@ func goexperiments(exps ...string) string { return existing + strings.Join(exps, ",") @@ -341,17 +327,17 @@ index aadb1ff52b067f..53d661338f2d72 100644 + return enabled +} diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go -index 47839e0229b951..2d1a7fa99d551e 100644 +index 47839e0229b951..99ac16a3951934 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go -@@ -13,6 +13,7 @@ import ( - "fmt" +@@ -14,6 +14,7 @@ import ( "go/format" "internal/godebug" -+ "internal/goexperiment" "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 @@ -370,17 +356,17 @@ index 47839e0229b951..2d1a7fa99d551e 100644 } func TestGoEnv(t *testing.T) { -+ if goexperiment.SystemCrypto { ++ 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..dcf9d04dd979f3 100644 +index 9bdbfb29c2752b..80c8211ed03100 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go -@@ -19,6 +19,7 @@ import ( +@@ -20,6 +20,7 @@ import ( "os" "path/filepath" "runtime" @@ -388,7 +374,7 @@ index 78cbbb949e7c27..dcf9d04dd979f3 100644 "strings" "sync" "time" -@@ -229,6 +230,21 @@ func ForceHost() { +@@ -230,6 +231,21 @@ func ForceHost() { BuildContext = defaultContext() // Call SetGOROOT to properly set the GOROOT on the new context. SetGOROOT(Getenv("GOROOT"), false) @@ -410,20 +396,6 @@ index 78cbbb949e7c27..dcf9d04dd979f3 100644 // 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 97c27c8caa44d6..bb18bb112a314a 100644 --- a/src/cmd/go/internal/tool/tool.go @@ -452,12 +424,25 @@ index 97c27c8caa44d6..bb18bb112a314a 100644 } 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..a1dfd3ac7842e8 100644 +--- a/src/cmd/go/internal/verylongtest/testdata/script/README ++++ b/src/cmd/go/internal/verylongtest/testdata/script/README +@@ -279,6 +279,8 @@ The available conditions are: + os.Geteuid() == 0 + [short] + testing.Short() ++[systemcrypto] ++ system crypto is enabled + [symlink] + testenv.HasSymlink() + [verbose] diff --git a/src/cmd/go/systemcrypto_test.go b/src/cmd/go/systemcrypto_test.go new file mode 100644 -index 00000000000000..d6cc434197cff1 +index 00000000000000..fa5c5b76024ab1 --- /dev/null +++ b/src/cmd/go/systemcrypto_test.go -@@ -0,0 +1,233 @@ +@@ -0,0 +1,250 @@ +// 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. @@ -469,7 +454,6 @@ index 00000000000000..d6cc434197cff1 + "internal/testenv" + "os" + "path/filepath" -+ "slices" + "strings" + "testing" +) @@ -500,9 +484,14 @@ index 00000000000000..d6cc434197cff1 +` + +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)) @@ -540,40 +529,29 @@ index 00000000000000..d6cc434197cff1 + cryptoFile := writeFile(t, fileWithCrypto) + + tt := []struct { -+ goos string -+ goarch string -+ goexp string -+ succeed bool ++ goos string ++ goarch string + }{ -+ {"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}, ++ {"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+"/"+tc.goexp, func(t *testing.T) { ++ t.Run(tc.goos+"/"+tc.goarch, 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") -+ } ++ 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) + } + }) + } @@ -625,9 +603,6 @@ index 00000000000000..d6cc434197cff1 + 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) @@ -636,95 +611,191 @@ index 00000000000000..d6cc434197cff1 + } + }) + } -+ +} + -+func TestSystemCryptoSetting(t *testing.T) { ++func TestSystemCryptoDisabled(t *testing.T) { + t.Parallel() -+ file := writeFile(t, fileWithCrypto) ++ cryptoFile := 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")}) ++ tests := []struct { ++ goos string ++ goarch string ++ }{ ++ {"linux", "amd64"}, ++ {"linux", "arm64"}, ++ {"darwin", "amd64"}, ++ {"darwin", "arm64"}, ++ {"windows", "amd64"}, ++ {"windows", "arm64"}, + } + for _, tt := range tests { -+ t.Run(strings.Join(tt.env, ","), func(t *testing.T) { ++ t.Run(fmt.Sprintf("%s_%s", tt.goos, tt.goarch), 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") ++ 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) + } ++ }) ++ } ++} + -+ // 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") -+ } ++func TestSystemCryptoBuildTag(t *testing.T) { ++ t.Parallel() + -+ // 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) ++ 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", ++ "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 ++ wantFile string ++ badFile string ++ }{ ++ { ++ name: "supported", ++ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFile: "with_system.go", ++ badFile: "without_system.go", ++ }, ++ { ++ name: "disabled", ++ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=1"}, ++ wantFile: "without_system.go", ++ badFile: "with_system.go", ++ }, ++ { ++ name: "unsupported", ++ env: []string{"GOOS=windows", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFile: "without_system.go", ++ badFile: "with_system.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}}", ".") ++ if !strings.Contains(out, tt.wantFile) || strings.Contains(out, tt.badFile) { ++ t.Fatalf("go list .GoFiles = %q, want %s and not %s", out, tt.wantFile, tt.badFile) + } + }) + } +} +diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README +index 9fa2c92c0b32b2..62484d97ee6bfe 100644 +--- a/src/cmd/go/testdata/script/README ++++ b/src/cmd/go/testdata/script/README +@@ -419,6 +419,8 @@ The available conditions are: + os.Geteuid() == 0 + [short] + testing.Short() ++[systemcrypto] ++ system crypto is enabled + [symlink] + testenv.HasSymlink() + [trimpath] +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_cross_build.txt b/src/cmd/go/testdata/script/env_cross_build.txt -index 91d1cb936d3652..b7f503d9506324 100644 +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). -+[GOEXPERIMENT:systemcrypto] skip # systemcrypto is not supported on ios nor mips ++[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 +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 @@ -+[GOEXPERIMENT:systemcrypto] skip # systemcrypto is not supported on android ++[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/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..bbeca248e4f8e4 100644 +index d81b3a849cb975..f051b6dfa209dd 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) { +@@ -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 slices.ContainsFunc(strings.Split(goExperiment, ","), func(s string) bool { -+ return s == "systemcrypto" -+ }) { ++ if systemcrypto.EnabledFor(goos, goarch) { + // Codegen tests cross-compile to platforms that do not support system crypto. + *allCodegen = false + } @@ -732,36 +803,23 @@ index d81b3a849cb975..bbeca248e4f8e4 100644 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 +index e7c406c4ad1eb8..7574d1b069807d 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go -@@ -13,6 +13,7 @@ import ( - "errors" +@@ -14,6 +14,7 @@ import ( "internal/abi" "internal/buildcfg" -+ "internal/goexperiment" "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 goexperiment.SystemCrypto { ++ if systemcrypto.Enabled() { + t.Skip("tvOS is not supported by system crypto") + } if testing.Short() && testenv.Builder() == "" { @@ -771,7 +829,7 @@ index e7c406c4ad1eb8..ac36a6dd1ca1a0 100644 t.Skip("skipping on non-{linux,darwin}/amd64 platform") } -+ if goexperiment.SystemCrypto { ++ if systemcrypto.Enabled() { + t.Skip("skipping on system crypto, which does not support android/386") + } + @@ -3326,7 +3384,7 @@ index a8d840b17022cc..2a17f7da2d4aaa 100644 } diff --git a/src/crypto/systemcrypto_nocgo_linux.go b/src/crypto/systemcrypto_nocgo_linux.go new file mode 100644 -index 00000000000000..7500bd3a86472b +index 00000000000000..b3a50c0aae95af --- /dev/null +++ b/src/crypto/systemcrypto_nocgo_linux.go @@ -0,0 +1,18 @@ @@ -3340,7 +3398,7 @@ index 00000000000000..7500bd3a86472b + +func init() { + ` -+ Using GOEXPERIMENT=systemcrypto on Linux requires CGO_ENABLED=1. ++ 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. @@ -3404,96 +3462,6 @@ index 7834d307b0426d..148fff74886a58 100644 < 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 diff --git a/patches/0004-Use-crypto-backends.patch b/patches/0004-Use-crypto-backends.patch index 58765e91b1..d339e52801 100644 --- a/patches/0004-Use-crypto-backends.patch +++ b/patches/0004-Use-crypto-backends.patch @@ -5,12 +5,11 @@ Subject: [PATCH] Use crypto backends --- src/cmd/api/boring_test.go | 2 +- - src/cmd/dist/test.go | 5 +- + src/cmd/dist/test.go | 3 +- 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/config.go | 7 + src/cmd/link/internal/ld/lib.go | 6 + src/crypto/aes/aes.go | 2 +- src/crypto/aes/aes_test.go | 2 +- @@ -64,7 +63,7 @@ Subject: [PATCH] Use crypto backends 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.go | 12 +- src/crypto/rsa/boring_test.go | 2 +- src/crypto/rsa/fips.go | 94 +++++---- src/crypto/rsa/notboring.go | 4 +- @@ -102,7 +101,7 @@ Subject: [PATCH] Use crypto backends 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(-) + 97 files changed, 1623 insertions(+), 250 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 @@ -128,7 +127,7 @@ index f0e3575637c62a..9eab3b4e66e60b 100644 package main diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go -index 53d661338f2d72..f87b5a6c5fd7fe 100644 +index dc6981715e9ce1..89101acdbe6d73 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -722,7 +722,7 @@ func (t *tester) registerTests() { @@ -140,17 +139,8 @@ index 53d661338f2d72..f87b5a6c5fd7fe 100644 // Test standard crypto packages with fips140=on. t.registerTest("GOFIPS140=latest go test crypto/...", &goTest{ variant: "gofips140", -@@ -1382,7 +1382,7 @@ func (t *tester) registerCgoTests(heading string) { - } - } - -- // Doing a static link with boringcrypto gets -+ // Doing a static link with systemcrypto gets - // 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 @@ -1391,7 +1391,6 @@ func (t *tester) registerCgoTests(heading string) { - return "skipping static linking check on Linux when using systemcrypto to avoid C linker warning about getaddrinfo", true + return "skipping static linking check on Linux when using boringcrypto to avoid C linker warning about getaddrinfo", true } } - @@ -190,18 +180,6 @@ index 088b9a8b5fdb2e..6a27473d4ed1d6 100644 } // 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..995d53d0fed9aa 100644 --- a/src/cmd/go/testdata/script/gopath_std_vendor.txt @@ -222,24 +200,16 @@ index 4aaf46b5d0f0dc..995d53d0fed9aa 100644 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 +index acbebf28598a9c..6ef32735ddfd57 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 { +@@ -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 goexperiment.SystemCrypto { ++ if platform.SystemCryptoSupported(buildcfg.GOOS, buildcfg.GOARCH) && !buildcfg.SystemCryptoDisabled { + *mode = BuildModePIE + } else { + *mode = BuildModeExe @@ -1395,7 +1365,7 @@ index ed599ad2908dfb..b3106b9ed83811 100644 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 +index c8a23e3246a949..843dbee858a5de 100644 --- a/src/crypto/ed25519/ed25519_test.go +++ b/src/crypto/ed25519/ed25519_test.go @@ -9,12 +9,15 @@ import ( @@ -1407,7 +1377,7 @@ index c8a23e3246a949..02dc683fa1a1b4 100644 "crypto/rand" "crypto/sha512" "encoding/hex" -+ "internal/goexperiment" ++ "internal/systemcrypto" "log" "os" + "runtime" @@ -1419,7 +1389,7 @@ index c8a23e3246a949..02dc683fa1a1b4 100644 sig2 := Sign(priv[:], msg) - if !bytes.Equal(sig, sig2[:]) { -+ if !bytes.Equal(sig, sig2[:]) && !(goexperiment.SystemCrypto && runtime.GOOS == "darwin") { ++ if !bytes.Equal(sig, sig2[:]) && !(systemcrypto.Enabled() && runtime.GOOS == "darwin") { t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2) } @@ -2352,7 +2322,7 @@ index 0bc14be888d9d6..4172402e1bb620 100644 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 +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 @@ -2366,7 +2336,7 @@ index eb0ed14e243c6b..cd7223c89eeae5 100644 "crypto/sha1" "crypto/sha256" "hash" -+ "internal/goexperiment" ++ "internal/systemcrypto" "testing" ) @@ -2374,7 +2344,7 @@ index eb0ed14e243c6b..cd7223c89eeae5 100644 } func TestMaxKeyLength(t *testing.T) { -+ if goexperiment.SystemCrypto { ++ 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. @@ -2496,10 +2466,10 @@ index c4d2b0d382e7fc..ba8dc141117e59 100644 return } diff --git a/src/crypto/rsa/boring.go b/src/crypto/rsa/boring.go -index b9f9d3154f2589..27bdf13d2268ce 100644 +index b9f9d3154f2589..92b960f7219945 100644 --- a/src/crypto/rsa/boring.go +++ b/src/crypto/rsa/boring.go -@@ -2,15 +2,17 @@ +@@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -2514,18 +2484,17 @@ index b9f9d3154f2589..27bdf13d2268ce 100644 + 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 { +@@ -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 goexperiment.SystemCrypto && runtime.GOOS == "darwin" && (priv.Precomputed.Dp == nil || priv.Precomputed.Dq == nil || priv.Precomputed.Qinv == nil) { ++ if runtime.GOOS == "darwin" && (priv.Precomputed.Dp == nil || priv.Precomputed.Dq == nil || priv.Precomputed.Qinv == nil) { + priv.Precompute() + priv.Precomputed.fips = nil + } @@ -2827,7 +2796,7 @@ index c65552cd93526a..910416abe842f5 100644 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 +index e03f4ab06603c6..088f87ee6a536e 100644 --- a/src/crypto/rsa/pss_test.go +++ b/src/crypto/rsa/pss_test.go @@ -8,14 +8,17 @@ import ( @@ -2841,7 +2810,7 @@ index e03f4ab06603c6..a9d9daba2fd6ce 100644 "crypto/sha256" "crypto/sha512" "encoding/hex" -+ "internal/goexperiment" ++ "internal/systemcrypto" "math/big" "os" + "runtime" @@ -2852,7 +2821,7 @@ index e03f4ab06603c6..a9d9daba2fd6ce 100644 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 { ++ 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) @@ -3167,7 +3136,7 @@ index 069938a22dbc5a..8d0e06b86f4359 100644 } h := New224() diff --git a/src/crypto/sha256/sha256_test.go b/src/crypto/sha256/sha256_test.go -index a18a536ba2896f..7b3b37c3b0c10d 100644 +index a18a536ba2896f..be543811ea94d1 100644 --- a/src/crypto/sha256/sha256_test.go +++ b/src/crypto/sha256/sha256_test.go @@ -8,10 +8,13 @@ package sha256 @@ -3180,7 +3149,7 @@ index a18a536ba2896f..7b3b37c3b0c10d 100644 + "errors" "fmt" "hash" -+ "internal/goexperiment" ++ "internal/systemcrypto" "io" "testing" ) @@ -3277,7 +3246,7 @@ index a18a536ba2896f..7b3b37c3b0c10d 100644 - if allocs != 0 { - t.Fatalf("allocs = %v; want = 0", allocs) + maxAllocs := 0 -+ if goexperiment.SystemCrypto { ++ if systemcrypto.Enabled() { + maxAllocs = 2 + } + if allocs > float64(maxAllocs) { @@ -4476,22 +4445,22 @@ index 42211ed099ed1e..e6d185374ffe6d 100644 const testNXDOMAIN = "invalid.invalid." diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go -index 4f39d9bbe345a8..0a379aaf9c1859 100644 +index 4f39d9bbe345a8..b372f4077a3fe3 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go -@@ -14,6 +14,7 @@ import ( - "errors" +@@ -15,6 +15,7 @@ import ( "flag" "fmt" -+ "internal/goexperiment" "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 goexperiment.SystemCrypto && runtime.GOOS == "linux" { ++ 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, diff --git a/patches/0009-Add-appinsights-telemetry.patch b/patches/0009-Add-appinsights-telemetry.patch index a5118539d8..1e48c16b69 100644 --- a/patches/0009-Add-appinsights-telemetry.patch +++ b/patches/0009-Add-appinsights-telemetry.patch @@ -40,15 +40,15 @@ 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..0cd7d3582c9ee6 100644 +index c6e58935bb0ee2..82651441fe61fd 100644 --- a/src/cmd/go/internal/telemetrystats/telemetrystats.go +++ b/src/cmd/go/internal/telemetrystats/telemetrystats.go @@ -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" + @@ -65,10 +65,10 @@ index c6e58935bb0ee2..0cd7d3582c9ee6 100644 + } + + 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") + } + } + diff --git a/patches/0013-Add-Microsoft-version-information.patch b/patches/0013-Add-Microsoft-version-information.patch index f8ae5f87ae..7c03c5ceb3 100644 --- a/patches/0013-Add-Microsoft-version-information.patch +++ b/patches/0013-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}, From bd74ddb808d1d727908fbeb719f1d98e992436dc Mon Sep 17 00:00:00 2001 From: "bot-for-go[bot]" <199222863+bot-for-go[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 09:00:54 +0000 Subject: [PATCH 22/36] Update submodule to latest master (01534385): cmd/go: allow defaults for removed GODEBUG settings --- go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go b/go index 7e3a04fb91..015343854b 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit 7e3a04fb9140963ce1c60bd3fda6f83833ca8b7d +Subproject commit 015343854b5d9e2829481df30dbcae2ca6682d25 From a695dbab7e37ec310a1468c9c3ef940ab00c2e61 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 2 Jun 2026 10:59:14 +0200 Subject: [PATCH 23/36] Update script README for systemcrypto condition --- ...03-Implement-crypto-internal-backend.patch | 68 +++++++++++++++---- patches/0004-Use-crypto-backends.patch | 15 ++-- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/patches/0003-Implement-crypto-internal-backend.patch b/patches/0003-Implement-crypto-internal-backend.patch index b3f65900f9..d69ae958d8 100644 --- a/patches/0003-Implement-crypto-internal-backend.patch +++ b/patches/0003-Implement-crypto-internal-backend.patch @@ -12,6 +12,7 @@ desired goexperiments and build tags. .gitignore | 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 | 45 +- src/cmd/go/go_test.go | 11 + @@ -23,9 +24,11 @@ desired goexperiments and build tags. src/cmd/go/testdata/script/darwin_no_cgo.txt | 1 + .../go/testdata/script/env_cross_build.txt | 2 + .../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/link_test.go | 8 + + src/cmd/link/testdata/script/README | 2 + src/crypto/internal/backend/backend_darwin.go | 442 ++++++++++++++++++ src/crypto/internal/backend/backend_linux.go | 435 +++++++++++++++++ src/crypto/internal/backend/backend_test.go | 56 +++ @@ -55,7 +58,7 @@ desired goexperiments and build tags. src/crypto/systemcrypto_nocgo_linux.go | 18 + src/go/build/deps_test.go | 24 +- src/runtime/runtime_boring.go | 5 + - 46 files changed, 2857 insertions(+), 17 deletions(-) + 49 files changed, 2863 insertions(+), 17 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 @@ -146,6 +149,19 @@ index 0e32e0769ee2e2..552dfe13dd6a05 100644 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 @@ -425,18 +441,18 @@ index 97c27c8caa44d6..bb18bb112a314a 100644 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..a1dfd3ac7842e8 100644 +index 9ec997a138f0f2..09c4e7955ec162 100644 --- a/src/cmd/go/internal/verylongtest/testdata/script/README +++ b/src/cmd/go/internal/verylongtest/testdata/script/README -@@ -279,6 +279,8 @@ The available conditions are: - os.Geteuid() == 0 - [short] +@@ -281,6 +281,8 @@ The available conditions are: testing.Short() -+[systemcrypto] -+ system crypto is enabled [symlink] testenv.HasSymlink() ++[systemcrypto] ++ system crypto is enabled [verbose] + testing.Verbose() + diff --git a/src/cmd/go/systemcrypto_test.go b/src/cmd/go/systemcrypto_test.go new file mode 100644 index 00000000000000..fa5c5b76024ab1 @@ -694,18 +710,18 @@ index 00000000000000..fa5c5b76024ab1 + } +} diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README -index 9fa2c92c0b32b2..62484d97ee6bfe 100644 +index 9fa2c92c0b32b2..c0dd9e533e8e48 100644 --- a/src/cmd/go/testdata/script/README +++ b/src/cmd/go/testdata/script/README -@@ -419,6 +419,8 @@ The available conditions are: - os.Geteuid() == 0 - [short] +@@ -421,6 +421,8 @@ The available conditions are: testing.Short() -+[systemcrypto] -+ system crypto is enabled [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 @@ -741,6 +757,19 @@ index 2f46a6b44bffe1..b036dde70ed36e 100644 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 @@ -836,6 +865,19 @@ index e7c406c4ad1eb8..7574d1b069807d 100644 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/internal/backend/backend_darwin.go b/src/crypto/internal/backend/backend_darwin.go new file mode 100644 index 00000000000000..73830cae246cbd diff --git a/patches/0004-Use-crypto-backends.patch b/patches/0004-Use-crypto-backends.patch index d339e52801..af257a41a9 100644 --- a/patches/0004-Use-crypto-backends.patch +++ b/patches/0004-Use-crypto-backends.patch @@ -7,7 +7,7 @@ Subject: [PATCH] Use crypto backends src/cmd/api/boring_test.go | 2 +- src/cmd/dist/test.go | 3 +- src/cmd/go/go_boring_test.go | 6 +- - src/cmd/go/script_test.go | 2 + + src/cmd/go/script_test.go | 3 + .../go/testdata/script/gopath_std_vendor.txt | 9 + src/cmd/link/internal/ld/config.go | 7 + src/cmd/link/internal/ld/lib.go | 6 + @@ -101,7 +101,7 @@ Subject: [PATCH] Use crypto backends src/hash/notboring_test.go | 9 + src/net/lookup_test.go | 3 + src/os/exec/exec_test.go | 9 + - 97 files changed, 1623 insertions(+), 250 deletions(-) + 97 files changed, 1624 insertions(+), 250 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 @@ -168,20 +168,21 @@ index ed0fbf3d53d75b..a2f74c8095d449 100644 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 +index 088b9a8b5fdb2e..4e1e545367afef 100644 --- a/src/cmd/go/script_test.go +++ b/src/cmd/go/script_test.go -@@ -299,6 +299,8 @@ var extraEnvKeys = []string{ +@@ -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/testdata/script/gopath_std_vendor.txt b/src/cmd/go/testdata/script/gopath_std_vendor.txt -index 4aaf46b5d0f0dc..995d53d0fed9aa 100644 +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 @@ @@ -193,8 +194,8 @@ index 4aaf46b5d0f0dc..995d53d0fed9aa 100644 +# 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 ++# dependency chain that would cause the failure if systemcrypto isn't disabled. ++env MS_GO_NOSYSTEMCRYPTO=1 + [!compiler:gc] skip From 82f2cd517f0ab1d4db4063a966e6543764553183 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 2 Jun 2026 14:00:53 +0200 Subject: [PATCH 24/36] merge backend patches --- ...002-Add-crypto-backend-GOEXPERIMENTs.patch | 578 --- ...s.patch => 0002-Add-crypto-backends.patch} | 4109 ++++++++++++++++- ...=> 0003-Disable-GOTOOLCHAIN-support.patch} | 0 ...03-Implement-crypto-internal-backend.patch | 3519 -------------- ...pport-for-logging-used-Windows-APIs.patch} | 0 ... 0005-Remove-long-path-support-hack.patch} | 0 ...ernal-go.mod-files-used-for-codegen.patch} | 0 ...h => 0007-Add-appinsights-telemetry.patch} | 0 ...t-ms_tls_config_schannel-experiment.patch} | 0 ...-Set-ms-go-build-as-default-GOCACHE.patch} | 0 ...LS-settings-with-Microsoft-policies.patch} | 0 ...1-Add-Microsoft-version-information.patch} | 0 ....patch => 0012-skip-sanitizer-tests.patch} | 0 ...ests.patch => 0013-Skip-http2-tests.patch} | 0 14 files changed, 4070 insertions(+), 4136 deletions(-) delete mode 100644 patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch rename patches/{0004-Use-crypto-backends.patch => 0002-Add-crypto-backends.patch} (50%) rename patches/{0005-Disable-GOTOOLCHAIN-support.patch => 0003-Disable-GOTOOLCHAIN-support.patch} (100%) delete mode 100644 patches/0003-Implement-crypto-internal-backend.patch rename patches/{0006-Add-support-for-logging-used-Windows-APIs.patch => 0004-Add-support-for-logging-used-Windows-APIs.patch} (100%) rename patches/{0007-Remove-long-path-support-hack.patch => 0005-Remove-long-path-support-hack.patch} (100%) rename patches/{0008-Omit-internal-go.mod-files-used-for-codegen.patch => 0006-Omit-internal-go.mod-files-used-for-codegen.patch} (100%) rename patches/{0009-Add-appinsights-telemetry.patch => 0007-Add-appinsights-telemetry.patch} (100%) rename patches/{0010-Implement-ms_tls_config_schannel-experiment.patch => 0008-Implement-ms_tls_config_schannel-experiment.patch} (100%) rename patches/{0011-Set-ms-go-build-as-default-GOCACHE.patch => 0009-Set-ms-go-build-as-default-GOCACHE.patch} (100%) rename patches/{0012-Align-TLS-settings-with-Microsoft-policies.patch => 0010-Align-TLS-settings-with-Microsoft-policies.patch} (100%) rename patches/{0013-Add-Microsoft-version-information.patch => 0011-Add-Microsoft-version-information.patch} (100%) rename patches/{0014-skip-sanitizer-tests.patch => 0012-skip-sanitizer-tests.patch} (100%) rename patches/{0015-Skip-http2-tests.patch => 0013-Skip-http2-tests.patch} (100%) diff --git a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch b/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch deleted file mode 100644 index 26b349c8ac..0000000000 --- a/patches/0002-Add-crypto-backend-GOEXPERIMENTs.patch +++ /dev/null @@ -1,578 +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/cfg/cfg.go | 6 +- - 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/testdata/script/env_changed.txt | 3 + - src/go/build/buildbackend_test.go | 50 +++++++++++++ - src/go/build/deps_test.go | 2 + - .../build/testdata/backendtags_system/main.go | 3 + - .../backendtags_system/systemcrypto.go | 3 + - 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 +++++++++++++++ - 18 files changed, 308 insertions(+), 2 deletions(-) - 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/systemcrypto/systemcrypto.go - create mode 100644 src/internal/systemcrypto/systemcrypto_test.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/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go -index 78cbbb949e7c27..9bdbfb29c2752b 100644 ---- a/src/cmd/go/internal/cfg/cfg.go -+++ b/src/cmd/go/internal/cfg/cfg.go -@@ -14,6 +14,7 @@ import ( - "internal/buildcfg" - "internal/cfg" - "internal/platform" -+ "internal/systemcrypto" - "io" - "io/fs" - "os" -@@ -311,10 +312,13 @@ 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)+1) - for _, exp := range exps { - expTags = append(expTags, "goexperiment."+exp) - } -+ if systemcrypto.EnabledFor(Goos, Goarch) { -+ expTags = append(expTags, "goexperiment.systemcrypto") -+ } - 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 1d90061b065f5f..ba582cf1d0773e 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" -@@ -2417,6 +2418,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/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/deps_test.go b/src/go/build/deps_test.go -index 920e72e5cdbb9d..7834d307b0426d 100644 ---- a/src/go/build/deps_test.go -+++ b/src/go/build/deps_test.go -@@ -370,6 +370,8 @@ var depsRules = ` - - FMT, internal/goexperiment - < 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 -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/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/patches/0004-Use-crypto-backends.patch b/patches/0002-Add-crypto-backends.patch similarity index 50% rename from patches/0004-Use-crypto-backends.patch rename to patches/0002-Add-crypto-backends.patch index af257a41a9..370cc5cb98 100644 --- a/patches/0004-Use-crypto-backends.patch +++ b/patches/0002-Add-crypto-backends.patch @@ -1,86 +1,138 @@ 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 +Date: Wed, 31 May 2023 16:54:31 -0500 +Subject: [PATCH] Add crypto backends --- + .gitignore | 2 + src/cmd/api/boring_test.go | 2 +- - src/cmd/dist/test.go | 3 +- + .../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 | 22 +- + 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 | 250 ++++++++++ + 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/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.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/boring.go | 113 +++++ + src/crypto/dsa/dsa.go | 88 ++++ src/crypto/dsa/dsa_test.go | 8 + - src/crypto/dsa/notboring.go | 16 ++ + 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/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/boring.go | 71 +++ + src/crypto/ed25519/ed25519.go | 70 +++ src/crypto/ed25519/ed25519_test.go | 14 +- - src/crypto/ed25519/notboring.go | 16 ++ + 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.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 | 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 + 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 ++-- + .../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.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/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/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.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/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/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 +- @@ -88,31 +140,88 @@ Subject: [PATCH] Use crypto backends 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/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.go | 24 + src/crypto/tls/prf_test.go | 9 + src/crypto/x509/verify_test.go | 2 +- - src/go/build/deps_test.go | 13 +- + 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 + - 97 files changed, 1624 insertions(+), 250 deletions(-) + src/runtime/runtime_boring.go | 5 + + 160 files changed, 4795 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/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/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 @@ -126,11 +235,189 @@ index f0e3575637c62a..9eab3b4e66e60b 100644 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 dc6981715e9ce1..89101acdbe6d73 100644 +index aadb1ff52b067f..89101acdbe6d73 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go -@@ -722,7 +722,7 @@ func (t *tester) registerTests() { +@@ -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. @@ -139,7 +426,35 @@ index dc6981715e9ce1..89101acdbe6d73 100644 // Test standard crypto packages with fips140=on. t.registerTest("GOFIPS140=latest go test crypto/...", &goTest{ variant: "gofips140", -@@ -1391,7 +1391,6 @@ func (t *tester) registerCgoTests(heading string) { +@@ -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 } } @@ -147,6 +462,49 @@ index dc6981715e9ce1..89101acdbe6d73 100644 // 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 @@ -167,6 +525,274 @@ index ed0fbf3d53d75b..a2f74c8095d449 100644 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..80c8211ed03100 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,13 @@ 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)+1) + for _, exp := range exps { + expTags = append(expTags, "goexperiment."+exp) + } ++ if systemcrypto.EnabledFor(Goos, Goarch) { ++ expTags = append(expTags, "goexperiment.systemcrypto") ++ } + 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 1d90061b065f5f..ba582cf1d0773e 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" +@@ -2417,6 +2418,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 @@ -181,6 +807,328 @@ index 088b9a8b5fdb2e..4e1e545367afef 100644 } // 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..fa5c5b76024ab1 +--- /dev/null ++++ b/src/cmd/go/systemcrypto_test.go +@@ -0,0 +1,250 @@ ++// 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", ++ "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 ++ wantFile string ++ badFile string ++ }{ ++ { ++ name: "supported", ++ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFile: "with_system.go", ++ badFile: "without_system.go", ++ }, ++ { ++ name: "disabled", ++ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=1"}, ++ wantFile: "without_system.go", ++ badFile: "with_system.go", ++ }, ++ { ++ name: "unsupported", ++ env: []string{"GOOS=windows", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"}, ++ wantFile: "without_system.go", ++ badFile: "with_system.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}}", ".") ++ if !strings.Contains(out, tt.wantFile) || strings.Contains(out, tt.badFile) { ++ t.Fatalf("go list .GoFiles = %q, want %s and not %s", out, tt.wantFile, tt.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 @@ -200,6 +1148,91 @@ index 4aaf46b5d0f0dc..46d03fcd4aeae3 100644 [!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 @@ -235,6 +1268,52 @@ index 9cbc12919f1d00..e64a9c5299e95a 100644 "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 @@ -1627,6 +2706,2527 @@ index fb55c97ddf20c9..fb036863cde2dd 100644 } 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..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..d8073b4f6c282e +--- /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.FIPSCapable to determine whether FIPS mode is enabled. ++ if err := fips140.Check(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..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/cryptotest/allocations.go b/src/crypto/internal/cryptotest/allocations.go index 70055af70b42ec..21528faa0d6145 100644 --- a/src/crypto/internal/cryptotest/allocations.go @@ -1734,6 +5334,31 @@ index 6d67ee8b3429a1..8f8d5937ea913c 100644 // 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 @@ -3830,6 +7455,30 @@ index 080bf694f03652..389e2738a4e885 100644 }) }) } +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 @@ -4321,11 +7970,79 @@ index 1d3e845d0f0a9c..eb4318faf13f7b 100644 } 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 148fff74886a58..5d3f4a9850e259 100644 +index 920e72e5cdbb9d..5d3f4a9850e259 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go -@@ -535,7 +535,7 @@ var depsRules = ` +@@ -368,8 +368,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 +@@ -533,7 +535,7 @@ var depsRules = ` < crypto/internal/fips140/edwards25519 < crypto/internal/fips140/ed25519 < crypto/internal/fips140/rsa @@ -4334,11 +8051,15 @@ index 148fff74886a58..5d3f4a9850e259 100644 crypto !< FIPS; -@@ -559,7 +559,12 @@ var depsRules = ` - < crypto/internal/backend/internal/opensslsetup - < crypto/internal/backend/fips140; +@@ -553,7 +555,16 @@ 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 ++ < crypto/internal/backend/fips140; ++ + internal/godebug, crypto/internal/fips140, crypto/internal/fips140/check, + crypto/internal/backend/fips140 + < crypto/fips140; @@ -4348,7 +8069,21 @@ index 148fff74886a58..5d3f4a9850e259 100644 < crypto/internal/fips140only < crypto < crypto/subtle -@@ -593,7 +598,8 @@ var depsRules = ` +@@ -573,6 +584,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, +@@ -580,7 +598,8 @@ var depsRules = ` crypto/internal/fips140only, crypto, crypto/subtle, @@ -4358,7 +8093,7 @@ index 148fff74886a58..5d3f4a9850e259 100644 < crypto/sha3 < crypto/internal/fips140hash < crypto/internal/boring -@@ -612,6 +618,7 @@ var depsRules = ` +@@ -599,6 +618,7 @@ var depsRules = ` crypto/ecdh, crypto/mlkem, crypto/mldsa @@ -4366,6 +8101,42 @@ index 148fff74886a58..5d3f4a9850e259 100644 < CRYPTO; CRYPTO +@@ -612,8 +632,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 @@ -4431,6 +8202,253 @@ index 00000000000000..11dc691600b110 +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 @@ -4472,3 +8490,16 @@ index 4f39d9bbe345a8..b372f4077a3fe3 100644 // 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 d69ae958d8..0000000000 --- a/patches/0003-Implement-crypto-internal-backend.patch +++ /dev/null @@ -1,3519 +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/compile/testdata/script/README | 2 + - src/cmd/dist/build.go | 68 ++- - src/cmd/dist/test.go | 45 +- - src/cmd/go/go_test.go | 11 + - src/cmd/go/internal/cfg/cfg.go | 16 + - src/cmd/go/internal/tool/tool.go | 9 +- - .../verylongtest/testdata/script/README | 2 + - src/cmd/go/systemcrypto_test.go | 250 ++++++++++ - src/cmd/go/testdata/script/README | 2 + - src/cmd/go/testdata/script/darwin_no_cgo.txt | 1 + - .../go/testdata/script/env_cross_build.txt | 2 + - .../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/link_test.go | 8 + - src/cmd/link/testdata/script/README | 2 + - 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/runtime/runtime_boring.go | 5 + - 49 files changed, 2863 insertions(+), 17 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..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..dc6981715e9ce1 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. -+ !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,7 +1386,7 @@ 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 - } -@@ -1908,3 +1914,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/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 9bdbfb29c2752b..80c8211ed03100 100644 ---- a/src/cmd/go/internal/cfg/cfg.go -+++ b/src/cmd/go/internal/cfg/cfg.go -@@ -20,6 +20,7 @@ import ( - "os" - "path/filepath" - "runtime" -+ "runtime/debug" - "strings" - "sync" - "time" -@@ -230,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. -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/systemcrypto_test.go b/src/cmd/go/systemcrypto_test.go -new file mode 100644 -index 00000000000000..fa5c5b76024ab1 ---- /dev/null -+++ b/src/cmd/go/systemcrypto_test.go -@@ -0,0 +1,250 @@ -+// 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", -+ "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 -+ wantFile string -+ badFile string -+ }{ -+ { -+ name: "supported", -+ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFile: "with_system.go", -+ badFile: "without_system.go", -+ }, -+ { -+ name: "disabled", -+ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=1"}, -+ wantFile: "without_system.go", -+ badFile: "with_system.go", -+ }, -+ { -+ name: "unsupported", -+ env: []string{"GOOS=windows", "GOARCH=386", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFile: "without_system.go", -+ badFile: "with_system.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}}", ".") -+ if !strings.Contains(out, tt.wantFile) || strings.Contains(out, tt.badFile) { -+ t.Fatalf("go list .GoFiles = %q, want %s and not %s", out, tt.wantFile, tt.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_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/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/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/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..d8073b4f6c282e ---- /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.FIPSCapable to determine whether FIPS mode is enabled. -+ if err := fips140.Check(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..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..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/go/build/deps_test.go b/src/go/build/deps_test.go -index 7834d307b0426d..148fff74886a58 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; - internal/buildcfg - < internal/systemcrypto; -@@ -555,6 +555,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 -@@ -575,6 +579,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, -@@ -614,8 +625,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/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/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 100% rename from patches/0009-Add-appinsights-telemetry.patch rename to patches/0007-Add-appinsights-telemetry.patch 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 100% rename from patches/0013-Add-Microsoft-version-information.patch rename to patches/0011-Add-Microsoft-version-information.patch diff --git a/patches/0014-skip-sanitizer-tests.patch b/patches/0012-skip-sanitizer-tests.patch similarity index 100% rename from patches/0014-skip-sanitizer-tests.patch rename to patches/0012-skip-sanitizer-tests.patch diff --git a/patches/0015-Skip-http2-tests.patch b/patches/0013-Skip-http2-tests.patch similarity index 100% rename from patches/0015-Skip-http2-tests.patch rename to patches/0013-Skip-http2-tests.patch From ffcfe32c9b69a2866a7529292eccea0c561df729 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 2 Jun 2026 16:05:11 +0200 Subject: [PATCH 25/36] Support legacy crypto build tags --- eng/doc/fips/README.md | 6 +- patches/0002-Add-crypto-backends.patch | 104 ++++++++++++++++--------- 2 files changed, 70 insertions(+), 40 deletions(-) diff --git a/eng/doc/fips/README.md b/eng/doc/fips/README.md index 6f766e6f43..92819eab3a 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/patches/0002-Add-crypto-backends.patch b/patches/0002-Add-crypto-backends.patch index 370cc5cb98..cc00f15451 100644 --- a/patches/0002-Add-crypto-backends.patch +++ b/patches/0002-Add-crypto-backends.patch @@ -14,7 +14,7 @@ Subject: [PATCH] Add crypto backends 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 | 22 +- + 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 + @@ -22,7 +22,7 @@ Subject: [PATCH] Add crypto backends 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 | 250 ++++++++++ + 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 + @@ -164,7 +164,7 @@ Subject: [PATCH] Add crypto backends src/net/lookup_test.go | 3 + src/os/exec/exec_test.go | 9 + src/runtime/runtime_boring.go | 5 + - 160 files changed, 4795 insertions(+), 269 deletions(-) + 160 files changed, 4827 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 @@ -562,7 +562,7 @@ index 47839e0229b951..99ac16a3951934 100644 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..80c8211ed03100 100644 +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 ( @@ -601,17 +601,27 @@ index 78cbbb949e7c27..80c8211ed03100 100644 // 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,13 @@ func computeExperiment() { +@@ -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)+1) ++ 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...) } @@ -652,7 +662,7 @@ index 4f76f7f9c1fc68..783d11609c0bf4 100644 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 1d90061b065f5f..ba582cf1d0773e 100644 +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 ( @@ -663,7 +673,7 @@ index 1d90061b065f5f..ba582cf1d0773e 100644 "io/fs" "os" pathpkg "path" -@@ -2417,6 +2418,9 @@ func (p *Package) setBuildInfo(ctx context.Context, f *modfetch.Fetcher, autoVCS +@@ -2448,6 +2449,9 @@ func (p *Package) setBuildInfo(ctx context.Context, f *modfetch.Fetcher, autoVCS buildmode = "archive" } } @@ -809,10 +819,10 @@ index 088b9a8b5fdb2e..4e1e545367afef 100644 // 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..fa5c5b76024ab1 +index 00000000000000..eeb0ee9c3c9396 --- /dev/null +++ b/src/cmd/go/systemcrypto_test.go -@@ -0,0 +1,250 @@ +@@ -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. @@ -1020,6 +1030,9 @@ index 00000000000000..fa5c5b76024ab1 + "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 { @@ -1029,36 +1042,55 @@ index 00000000000000..fa5c5b76024ab1 + } + + tests := []struct { -+ name string -+ env []string -+ wantFile string -+ badFile string ++ name string ++ env []string ++ wantFiles []string ++ badFiles []string + }{ + { -+ name: "supported", -+ env: []string{"GOOS=linux", "GOARCH=amd64", "MS_GO_NOSYSTEMCRYPTO=0"}, -+ wantFile: "with_system.go", -+ badFile: "without_system.go", ++ 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"}, -+ wantFile: "without_system.go", -+ badFile: "with_system.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"}, -+ wantFile: "without_system.go", -+ badFile: "with_system.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}}", ".") -+ if !strings.Contains(out, tt.wantFile) || strings.Contains(out, tt.badFile) { -+ t.Fatalf("go list .GoFiles = %q, want %s and not %s", out, tt.wantFile, tt.badFile) ++ 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) ++ } + } + }) + } @@ -8027,10 +8059,10 @@ index 00000000000000..ffb835ce34a2f7 + } +} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index 920e72e5cdbb9d..5d3f4a9850e259 100644 +index a71759adcb7363..5ede9a29a82750 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go -@@ -368,8 +368,10 @@ var depsRules = ` +@@ -372,8 +372,10 @@ var depsRules = ` math/big, go/token < go/constant; @@ -8042,7 +8074,7 @@ index 920e72e5cdbb9d..5d3f4a9850e259 100644 # 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 -@@ -533,7 +535,7 @@ var depsRules = ` +@@ -537,7 +539,7 @@ var depsRules = ` < crypto/internal/fips140/edwards25519 < crypto/internal/fips140/ed25519 < crypto/internal/fips140/rsa @@ -8051,7 +8083,7 @@ index 920e72e5cdbb9d..5d3f4a9850e259 100644 crypto !< FIPS; -@@ -553,7 +555,16 @@ var depsRules = ` +@@ -557,7 +559,16 @@ var depsRules = ` < github.com/microsoft/go-crypto-winnative/internal/sysdll < github.com/microsoft/go-crypto-winnative/internal/bcrypt; @@ -8069,7 +8101,7 @@ index 920e72e5cdbb9d..5d3f4a9850e259 100644 < crypto/internal/fips140only < crypto < crypto/subtle -@@ -573,6 +584,13 @@ var depsRules = ` +@@ -577,6 +588,13 @@ var depsRules = ` github.com/microsoft/go-crypto-winnative/internal/bcrypt < github.com/microsoft/go-crypto-winnative/cng; @@ -8083,7 +8115,7 @@ index 920e72e5cdbb9d..5d3f4a9850e259 100644 FIPS, internal/godebug, embed, crypto/internal/boring/sig, crypto/internal/boring/syso, -@@ -580,7 +598,8 @@ var depsRules = ` +@@ -584,7 +602,8 @@ var depsRules = ` crypto/internal/fips140only, crypto, crypto/subtle, @@ -8093,7 +8125,7 @@ index 920e72e5cdbb9d..5d3f4a9850e259 100644 < crypto/sha3 < crypto/internal/fips140hash < crypto/internal/boring -@@ -599,6 +618,7 @@ var depsRules = ` +@@ -603,6 +622,7 @@ var depsRules = ` crypto/ecdh, crypto/mlkem, crypto/mldsa @@ -8101,7 +8133,7 @@ index 920e72e5cdbb9d..5d3f4a9850e259 100644 < CRYPTO; CRYPTO -@@ -612,8 +632,15 @@ var depsRules = ` +@@ -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; From 99362e76489f9b33cc09bf4ca8f9674afb9eee11 Mon Sep 17 00:00:00 2001 From: George Adams Date: Tue, 2 Jun 2026 17:10:11 +0100 Subject: [PATCH 26/36] patches: unskip sanitizer tests (#2336) --- ...ests.patch => 0012-Skip-http2-tests.patch} | 0 patches/0012-skip-sanitizer-tests.patch | 36 ------------------- 2 files changed, 36 deletions(-) rename patches/{0013-Skip-http2-tests.patch => 0012-Skip-http2-tests.patch} (100%) delete mode 100644 patches/0012-skip-sanitizer-tests.patch diff --git a/patches/0013-Skip-http2-tests.patch b/patches/0012-Skip-http2-tests.patch similarity index 100% rename from patches/0013-Skip-http2-tests.patch rename to patches/0012-Skip-http2-tests.patch diff --git a/patches/0012-skip-sanitizer-tests.patch b/patches/0012-skip-sanitizer-tests.patch deleted file mode 100644 index a962a86f45..0000000000 --- a/patches/0012-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") From ca1f0f08e52779f665ac3201bcc3a0e6e9f55ba6 Mon Sep 17 00:00:00 2001 From: "bot-for-go[bot]" <199222863+bot-for-go[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 16:36:43 +0000 Subject: [PATCH 27/36] Update submodule to latest master (a81b20a4): crypto/internal/fips140deps/time: always use unbubbled monotonic clock --- go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go b/go index 015343854b..a81b20a431 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit 015343854b5d9e2829481df30dbcae2ca6682d25 +Subproject commit a81b20a431ce41417f7758929a2962d95a6a9453 From fff783512d9c25f66757e78f12b0e3912c59bd49 Mon Sep 17 00:00:00 2001 From: George Adams Date: Tue, 2 Jun 2026 18:30:48 +0100 Subject: [PATCH 28/36] unskip patch 12 --- patches/0012-Skip-http2-tests.patch | 89 ----------------------------- 1 file changed, 89 deletions(-) delete mode 100644 patches/0012-Skip-http2-tests.patch diff --git a/patches/0012-Skip-http2-tests.patch b/patches/0012-Skip-http2-tests.patch deleted file mode 100644 index b88d636900..0000000000 --- a/patches/0012-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) { From d7d59b3aedd0560421d669886ee34280b9a46b66 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 3 Jun 2026 11:02:19 +0200 Subject: [PATCH 29/36] reduce crypto/internal/backend/fips140 API surface --- patches/0002-Add-crypto-backends.patch | 341 +++++++++++++------------ 1 file changed, 177 insertions(+), 164 deletions(-) diff --git a/patches/0002-Add-crypto-backends.patch b/patches/0002-Add-crypto-backends.patch index cc00f15451..e624166db0 100644 --- a/patches/0002-Add-crypto-backends.patch +++ b/patches/0002-Add-crypto-backends.patch @@ -68,30 +68,31 @@ Subject: [PATCH] Add crypto backends 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 | 442 ++++++++++++++++++ + src/crypto/internal/backend/backend_darwin.go | 441 ++++++++++++++++++ src/crypto/internal/backend/backend_linux.go | 435 +++++++++++++++++ src/crypto/internal/backend/backend_test.go | 56 +++ - .../internal/backend/backend_windows.go | 442 ++++++++++++++++++ + .../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 | 46 ++ - .../internal/backend/fips140/fips140.go | 95 ++++ + src/crypto/internal/backend/fips140.go | 35 ++ + .../internal/backend/fips140/fips140.go | 78 ++++ .../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 ++ + .../backend/fips140/nosystemcrypto.go | 11 + + .../backend/fips140/systemfips_darwin.go | 11 + + .../backend/fips140/systemfips_linux.go | 57 +++ + .../backend/fips140/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 | 376 +++++++++++++++ + src/crypto/internal/backend/isrequirefips.go | 9 + + src/crypto/internal/backend/nobackend.go | 375 +++++++++++++++ + src/crypto/internal/backend/norequirefips.go | 9 + + .../backend/requirefips_nosystemcrypto.go | 15 + + .../internal/backend/skipfipscheck_off.go | 9 + + .../internal/backend/skipfipscheck_on.go | 9 + src/crypto/internal/backend/stub.s | 10 + src/crypto/internal/cryptotest/allocations.go | 6 - src/crypto/internal/cryptotest/fips140.go | 8 + @@ -164,7 +165,7 @@ Subject: [PATCH] Add crypto backends src/net/lookup_test.go | 3 + src/os/exec/exec_test.go | 9 + src/runtime/runtime_boring.go | 5 + - 160 files changed, 4827 insertions(+), 269 deletions(-) + 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 @@ -181,21 +182,22 @@ Subject: [PATCH] Add crypto backends 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.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/isrequirefips.go create mode 100644 src/crypto/internal/backend/nobackend.go + create mode 100644 src/crypto/internal/backend/norequirefips.go + create mode 100644 src/crypto/internal/backend/requirefips_nosystemcrypto.go + create mode 100644 src/crypto/internal/backend/skipfipscheck_off.go + create mode 100644 src/crypto/internal/backend/skipfipscheck_on.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 @@ -2740,10 +2742,10 @@ index fb55c97ddf20c9..fb036863cde2dd 100644 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..73830cae246cbd +index 00000000000000..f3d79d6b318d8a --- /dev/null +++ b/src/crypto/internal/backend/backend_darwin.go -@@ -0,0 +1,442 @@ +@@ -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. @@ -2758,7 +2760,6 @@ index 00000000000000..73830cae246cbd +import ( + "crypto" + "crypto/cipher" -+ "crypto/internal/backend/fips140" + "crypto/internal/boring/sig" + "crypto/internal/fips140only" + "errors" @@ -2771,7 +2772,7 @@ index 00000000000000..73830cae246cbd + +func init() { + // Darwin is considered FIPS compliant. -+ if err := fips140.Check(func() bool { return true }); err != nil { ++ if err := checkFIPS(func() bool { return true }); err != nil { + panic("darwincrypto: " + err.Error()) + } + sig.BoringCrypto() @@ -3188,7 +3189,7 @@ index 00000000000000..73830cae246cbd +} diff --git a/src/crypto/internal/backend/backend_linux.go b/src/crypto/internal/backend/backend_linux.go new file mode 100644 -index 00000000000000..d8073b4f6c282e +index 00000000000000..e97f2fa9837863 --- /dev/null +++ b/src/crypto/internal/backend/backend_linux.go @@ -0,0 +1,435 @@ @@ -3228,7 +3229,7 @@ index 00000000000000..d8073b4f6c282e + // 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 := fips140.Check(func() bool { return osslsetup.FIPSCapable() }); err != nil { ++ 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. @@ -3691,10 +3692,10 @@ index 00000000000000..093acd82556330 +} 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 +index 00000000000000..8013f189808e31 --- /dev/null +++ b/src/crypto/internal/backend/backend_windows.go -@@ -0,0 +1,442 @@ +@@ -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. @@ -3709,7 +3710,6 @@ index 00000000000000..d25ef26f77013d +import ( + "crypto" + "crypto/cipher" -+ "crypto/internal/backend/fips140" + "crypto/internal/boring/sig" + "crypto/internal/fips140only" + "errors" @@ -3721,7 +3721,7 @@ index 00000000000000..d25ef26f77013d + +func init() { + // Windows is considered FIPS compliant. -+ if err := fips140.Check(func() bool { return true }); err != nil { ++ if err := checkFIPS(func() bool { return true }); err != nil { + panic("cngcrypto: " + err.Error()) + } + sig.BoringCrypto() @@ -4266,12 +4266,53 @@ index 00000000000000..a9682181ffa154 + } + } +} +diff --git a/src/crypto/internal/backend/fips140.go b/src/crypto/internal/backend/fips140.go +new file mode 100644 +index 00000000000000..e306d756c39898 +--- /dev/null ++++ b/src/crypto/internal/backend/fips140.go +@@ -0,0 +1,35 @@ ++// 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 backend ++ ++import ( ++ "crypto/internal/backend/fips140" ++ "errors" ++ "runtime" ++) ++ ++func checkFIPS(fips func() bool) error { ++ if isRequireFIPS { ++ if isSkipFIPSCheck { ++ panic("the 'requirefips' build tag is enabled, but it conflicts " + ++ "with the 'ms_skipfipscheck' build tag") ++ } ++ fips140.Require() ++ } ++ if isSkipFIPSCheck || !fips140.Enabled() { ++ return nil ++ } ++ message := fips140.Message() ++ if !Enabled { ++ 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.go b/src/crypto/internal/backend/fips140/fips140.go new file mode 100644 -index 00000000000000..6c3568d77d2c0a +index 00000000000000..b7238d55d98060 --- /dev/null +++ b/src/crypto/internal/backend/fips140/fips140.go -@@ -0,0 +1,95 @@ +@@ -0,0 +1,78 @@ +// 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. @@ -4279,9 +4320,7 @@ index 00000000000000..6c3568d77d2c0a +package fips140 + +import ( -+ "errors" + "internal/godebug" -+ "runtime" + "syscall" +) + @@ -4293,6 +4332,17 @@ index 00000000000000..6c3568d77d2c0a + return enabled +} + ++// Message reports how [Enabled] was decided. ++func Message() string { ++ return message ++} ++ ++// Require records that FIPS 140 mode is required by the backend build configuration. ++func Require() { ++ message = "requirefips tag set" ++ enabled = true ++} ++ +var enabled bool + +// message is a human-readable message about how [Enabled] was set. @@ -4302,14 +4352,6 @@ index 00000000000000..6c3568d77d2c0a + // 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 @@ -4349,24 +4391,6 @@ index 00000000000000..6c3568d77d2c0a + } + 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 @@ -4448,44 +4472,12 @@ index 00000000000000..9fb7df809baed8 + }) + } +} -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 +index 00000000000000..83691d7dd42d51 --- /dev/null +++ b/src/crypto/internal/backend/fips140/nosystemcrypto.go -@@ -0,0 +1,13 @@ +@@ -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. @@ -4494,68 +4486,15 @@ index 00000000000000..289d6594eac54b + +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 +index 00000000000000..5e3c050681c06a --- /dev/null +++ b/src/crypto/internal/backend/fips140/systemfips_darwin.go -@@ -0,0 +1,13 @@ +@@ -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. @@ -4564,17 +4503,15 @@ index 00000000000000..606a2db4dd685f + +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 +index 00000000000000..519fd1410c86de --- /dev/null +++ b/src/crypto/internal/backend/fips140/systemfips_linux.go -@@ -0,0 +1,59 @@ +@@ -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. @@ -4590,8 +4527,6 @@ index 00000000000000..2c65c264dc84e1 + "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. @@ -4636,10 +4571,10 @@ index 00000000000000..2c65c264dc84e1 +} 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 +index 00000000000000..2de9c7aa35b427 --- /dev/null +++ b/src/crypto/internal/backend/fips140/systemfips_windows.go -@@ -0,0 +1,35 @@ +@@ -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. @@ -4654,8 +4589,6 @@ index 00000000000000..a705e7bc4fc536 + "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, @@ -4861,12 +4794,27 @@ index 00000000000000..19fd29e19e7b96 +// without cgo enabled or without goexperiment.systemcrypto on linux. + +package opensslsetup +diff --git a/src/crypto/internal/backend/isrequirefips.go b/src/crypto/internal/backend/isrequirefips.go +new file mode 100644 +index 00000000000000..0e2133479da834 +--- /dev/null ++++ b/src/crypto/internal/backend/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 backend ++ ++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..cdc768b1e4b57d +index 00000000000000..84a73fc5c2d523 --- /dev/null +++ b/src/crypto/internal/backend/nobackend.go -@@ -0,0 +1,376 @@ +@@ -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. @@ -4878,13 +4826,12 @@ index 00000000000000..cdc768b1e4b57d +import ( + "crypto" + "crypto/cipher" -+ "crypto/internal/backend/fips140" + "hash" + "io" +) + +func init() { -+ if err := fips140.Check(func() bool { return false }); err != nil { ++ if err := checkFIPS(func() bool { return false }); err != nil { + panic(err) + } +} @@ -5243,6 +5190,72 @@ index 00000000000000..cdc768b1e4b57d +func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { + panic("cryptobackend: not available") +} +diff --git a/src/crypto/internal/backend/norequirefips.go b/src/crypto/internal/backend/norequirefips.go +new file mode 100644 +index 00000000000000..ac174abe3cfde3 +--- /dev/null ++++ b/src/crypto/internal/backend/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 backend ++ ++const isRequireFIPS = false +diff --git a/src/crypto/internal/backend/requirefips_nosystemcrypto.go b/src/crypto/internal/backend/requirefips_nosystemcrypto.go +new file mode 100644 +index 00000000000000..f06bf33ba66295 +--- /dev/null ++++ b/src/crypto/internal/backend/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 backend ++ ++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/skipfipscheck_off.go b/src/crypto/internal/backend/skipfipscheck_off.go +new file mode 100644 +index 00000000000000..b11ecde5adcb67 +--- /dev/null ++++ b/src/crypto/internal/backend/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 backend ++ ++const isSkipFIPSCheck = false +diff --git a/src/crypto/internal/backend/skipfipscheck_on.go b/src/crypto/internal/backend/skipfipscheck_on.go +new file mode 100644 +index 00000000000000..5fd518cea78a24 +--- /dev/null ++++ b/src/crypto/internal/backend/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 backend ++ ++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 @@ -6035,7 +6048,7 @@ index 018fe013cef600..ec710c6f935e3b 100644 "crypto/internal/rand" "io" diff --git a/src/crypto/rand/rand_test.go b/src/crypto/rand/rand_test.go -index 13e4a65db28bd2..3404dd39d73c6f 100644 +index 596f6d434065fd..466de618fe2bc5 100644 --- a/src/crypto/rand/rand_test.go +++ b/src/crypto/rand/rand_test.go @@ -7,6 +7,7 @@ package rand @@ -6046,7 +6059,7 @@ index 13e4a65db28bd2..3404dd39d73c6f 100644 "crypto/internal/cryptotest" "crypto/internal/rand" "errors" -@@ -156,13 +157,17 @@ var sink byte +@@ -157,13 +158,17 @@ var sink byte func TestAllocations(t *testing.T) { cryptotest.SkipTestAllocations(t) From 21a763c5a8e197ae8965d0109bb806e8c7e2925d Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 3 Jun 2026 12:00:58 +0200 Subject: [PATCH 30/36] Address FIPS backend review feedback --- patches/0002-Add-crypto-backends.patch | 374 +++++++++++++------------ 1 file changed, 188 insertions(+), 186 deletions(-) diff --git a/patches/0002-Add-crypto-backends.patch b/patches/0002-Add-crypto-backends.patch index e624166db0..23c70418e4 100644 --- a/patches/0002-Add-crypto-backends.patch +++ b/patches/0002-Add-crypto-backends.patch @@ -69,30 +69,30 @@ Subject: [PATCH] Add crypto backends 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 | 435 +++++++++++++++++ +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 | 46 ++ - src/crypto/internal/backend/fips140.go | 35 ++ - .../internal/backend/fips140/fips140.go | 78 ++++ - .../internal/backend/fips140/fips140_test.go | 75 +++ - .../backend/fips140/nosystemcrypto.go | 11 + - .../backend/fips140/systemfips_darwin.go | 11 + - .../backend/fips140/systemfips_linux.go | 57 +++ - .../backend/fips140/systemfips_windows.go | 33 ++ + 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/isrequirefips.go | 9 + src/crypto/internal/backend/nobackend.go | 375 +++++++++++++++ - src/crypto/internal/backend/norequirefips.go | 9 + - .../backend/requirefips_nosystemcrypto.go | 15 + - .../internal/backend/skipfipscheck_off.go | 9 + - .../internal/backend/skipfipscheck_on.go | 9 + src/crypto/internal/backend/stub.s | 10 + src/crypto/internal/cryptotest/allocations.go | 6 - src/crypto/internal/cryptotest/fips140.go | 8 + @@ -182,22 +182,22 @@ Subject: [PATCH] Add crypto backends 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.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/nosystemcrypto.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/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/isrequirefips.go create mode 100644 src/crypto/internal/backend/nobackend.go - create mode 100644 src/crypto/internal/backend/norequirefips.go - create mode 100644 src/crypto/internal/backend/requirefips_nosystemcrypto.go - create mode 100644 src/crypto/internal/backend/skipfipscheck_off.go - create mode 100644 src/crypto/internal/backend/skipfipscheck_on.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 @@ -3189,10 +3189,10 @@ index 00000000000000..f3d79d6b318d8a +} diff --git a/src/crypto/internal/backend/backend_linux.go b/src/crypto/internal/backend/backend_linux.go new file mode 100644 -index 00000000000000..e97f2fa9837863 +index 00000000000000..c8c07a4beeecde --- /dev/null +++ b/src/crypto/internal/backend/backend_linux.go -@@ -0,0 +1,435 @@ +@@ -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. @@ -3208,7 +3208,6 @@ index 00000000000000..e97f2fa9837863 + "crypto" + "crypto/cipher" + "crypto/internal/backend/fips140" -+ _ "crypto/internal/backend/internal/opensslsetup" + "crypto/internal/boring/sig" + "crypto/internal/fips140only" + "errors" @@ -4216,10 +4215,10 @@ index 00000000000000..f2c21a88bff471 +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 +index 00000000000000..c5f983f68739aa --- /dev/null +++ b/src/crypto/internal/backend/common.go -@@ -0,0 +1,46 @@ +@@ -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. @@ -4227,10 +4226,15 @@ index 00000000000000..a9682181ffa154 +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() { @@ -4266,53 +4270,12 @@ index 00000000000000..a9682181ffa154 + } + } +} -diff --git a/src/crypto/internal/backend/fips140.go b/src/crypto/internal/backend/fips140.go -new file mode 100644 -index 00000000000000..e306d756c39898 ---- /dev/null -+++ b/src/crypto/internal/backend/fips140.go -@@ -0,0 +1,35 @@ -+// 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 backend -+ -+import ( -+ "crypto/internal/backend/fips140" -+ "errors" -+ "runtime" -+) -+ -+func checkFIPS(fips func() bool) error { -+ if isRequireFIPS { -+ if isSkipFIPSCheck { -+ panic("the 'requirefips' build tag is enabled, but it conflicts " + -+ "with the 'ms_skipfipscheck' build tag") -+ } -+ fips140.Require() -+ } -+ if isSkipFIPSCheck || !fips140.Enabled() { -+ return nil -+ } -+ message := fips140.Message() -+ if !Enabled { -+ 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.go b/src/crypto/internal/backend/fips140/fips140.go new file mode 100644 -index 00000000000000..b7238d55d98060 +index 00000000000000..436cb00f359bcf --- /dev/null +++ b/src/crypto/internal/backend/fips140/fips140.go -@@ -0,0 +1,78 @@ +@@ -0,0 +1,12 @@ +// 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. @@ -4320,88 +4283,25 @@ index 00000000000000..b7238d55d98060 +package fips140 + +import ( -+ "internal/godebug" -+ "syscall" ++ "crypto/internal/backend/internal/fips140state" +) + -+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. ++// Enabled reports whether FIPS 140 mode is enabled by using GODEBUG, GOFIPS, ++// GOLANG_FIPS, or any platform-specific mechanism. +func Enabled() bool { -+ return enabled -+} -+ -+// Message reports how [Enabled] was decided. -+func Message() string { -+ return message -+} -+ -+// Require records that FIPS 140 mode is required by the backend build configuration. -+func Require() { -+ message = "requirefips tag set" -+ enabled = true -+} -+ -+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) ++ return fips140state.Enabled() +} -+ -+// 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/fips140/fips140_test.go b/src/crypto/internal/backend/fips140/fips140_test.go +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..9fb7df809baed8 +index 00000000000000..254a27594b578a --- /dev/null -+++ b/src/crypto/internal/backend/fips140/fips140_test.go ++++ 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 fips140 ++package fips140state + +import ( + "testing" @@ -4472,11 +4372,11 @@ index 00000000000000..9fb7df809baed8 + }) + } +} -diff --git a/src/crypto/internal/backend/fips140/nosystemcrypto.go b/src/crypto/internal/backend/fips140/nosystemcrypto.go +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..83691d7dd42d51 +index 00000000000000..28ec07f7b20eaf --- /dev/null -+++ b/src/crypto/internal/backend/fips140/nosystemcrypto.go ++++ 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 @@ -4484,16 +4384,16 @@ index 00000000000000..83691d7dd42d51 + +//go:build !goexperiment.systemcrypto + -+package fips140 ++package fips140state + +func systemFIPSMode() bool { + return false +} -diff --git a/src/crypto/internal/backend/fips140/systemfips_darwin.go b/src/crypto/internal/backend/fips140/systemfips_darwin.go +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..5e3c050681c06a +index 00000000000000..a6fba27162a6ef --- /dev/null -+++ b/src/crypto/internal/backend/fips140/systemfips_darwin.go ++++ 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 @@ -4501,16 +4401,16 @@ index 00000000000000..5e3c050681c06a + +//go:build goexperiment.systemcrypto + -+package fips140 ++package fips140state + +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 +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..519fd1410c86de +index 00000000000000..051d709e557061 --- /dev/null -+++ b/src/crypto/internal/backend/fips140/systemfips_linux.go ++++ 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 @@ -4518,7 +4418,7 @@ index 00000000000000..519fd1410c86de + +//go:build goexperiment.systemcrypto + -+package fips140 ++package fips140state + +import ( + _ "crypto/internal/backend/internal/opensslsetup" @@ -4569,11 +4469,11 @@ index 00000000000000..519fd1410c86de + // 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 +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..2de9c7aa35b427 +index 00000000000000..2ef633391eb9ab --- /dev/null -+++ b/src/crypto/internal/backend/fips140/systemfips_windows.go ++++ 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 @@ -4581,7 +4481,7 @@ index 00000000000000..2de9c7aa35b427 + +//go:build goexperiment.systemcrypto + -+package fips140 ++package fips140state + +import ( + "internal/syscall/windows/sysdll" @@ -4608,6 +4508,103 @@ index 00000000000000..2de9c7aa35b427 + } + 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 @@ -4794,11 +4791,11 @@ index 00000000000000..19fd29e19e7b96 +// without cgo enabled or without goexperiment.systemcrypto on linux. + +package opensslsetup -diff --git a/src/crypto/internal/backend/isrequirefips.go b/src/crypto/internal/backend/isrequirefips.go +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..0e2133479da834 +index 00000000000000..122185bb417d3c --- /dev/null -+++ b/src/crypto/internal/backend/isrequirefips.go ++++ 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 @@ -4806,7 +4803,7 @@ index 00000000000000..0e2133479da834 + +//go:build requirefips + -+package backend ++package fips140state + +const isRequireFIPS = true diff --git a/src/crypto/internal/backend/nobackend.go b/src/crypto/internal/backend/nobackend.go @@ -5190,11 +5187,11 @@ index 00000000000000..84a73fc5c2d523 +func NewChaCha20Poly1305(key []byte) (cipher.AEAD, error) { + panic("cryptobackend: not available") +} -diff --git a/src/crypto/internal/backend/norequirefips.go b/src/crypto/internal/backend/norequirefips.go +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..ac174abe3cfde3 +index 00000000000000..1967f167d30436 --- /dev/null -+++ b/src/crypto/internal/backend/norequirefips.go ++++ 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 @@ -5202,14 +5199,14 @@ index 00000000000000..ac174abe3cfde3 + +//go:build !requirefips + -+package backend ++package fips140state + +const isRequireFIPS = false -diff --git a/src/crypto/internal/backend/requirefips_nosystemcrypto.go b/src/crypto/internal/backend/requirefips_nosystemcrypto.go +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..f06bf33ba66295 +index 00000000000000..4fe623e27e1d35 --- /dev/null -+++ b/src/crypto/internal/backend/requirefips_nosystemcrypto.go ++++ 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 @@ -5217,7 +5214,7 @@ index 00000000000000..f06bf33ba66295 + +//go:build requirefips && !goexperiment.systemcrypto + -+package backend ++package fips140state + +func init() { + ` @@ -5226,11 +5223,11 @@ index 00000000000000..f06bf33ba66295 + For more information, visit https://github.com/microsoft/go/tree/microsoft/main/eng/doc/fips + ` +} -diff --git a/src/crypto/internal/backend/skipfipscheck_off.go b/src/crypto/internal/backend/skipfipscheck_off.go +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..b11ecde5adcb67 +index 00000000000000..b8458bb58011d5 --- /dev/null -+++ b/src/crypto/internal/backend/skipfipscheck_off.go ++++ 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 @@ -5238,14 +5235,14 @@ index 00000000000000..b11ecde5adcb67 + +//go:build !ms_skipfipscheck + -+package backend ++package fips140state + +const isSkipFIPSCheck = false -diff --git a/src/crypto/internal/backend/skipfipscheck_on.go b/src/crypto/internal/backend/skipfipscheck_on.go +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..5fd518cea78a24 +index 00000000000000..d13b8a7d07916e --- /dev/null -+++ b/src/crypto/internal/backend/skipfipscheck_on.go ++++ 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 @@ -5253,7 +5250,7 @@ index 00000000000000..5fd518cea78a24 + +//go:build ms_skipfipscheck + -+package backend ++package fips140state + +const isSkipFIPSCheck = true diff --git a/src/crypto/internal/backend/stub.s b/src/crypto/internal/backend/stub.s @@ -8072,7 +8069,7 @@ index 00000000000000..ffb835ce34a2f7 + } +} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go -index a71759adcb7363..5ede9a29a82750 100644 +index a71759adcb7363..ffe6279a6942d7 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -372,8 +372,10 @@ var depsRules = ` @@ -8096,13 +8093,17 @@ index a71759adcb7363..5ede9a29a82750 100644 crypto !< FIPS; -@@ -557,7 +559,16 @@ var depsRules = ` +@@ -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 ++ < 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, @@ -8114,13 +8115,14 @@ index a71759adcb7363..5ede9a29a82750 100644 < crypto/internal/fips140only < crypto < crypto/subtle -@@ -577,6 +588,13 @@ var depsRules = ` +@@ -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; @@ -8128,7 +8130,7 @@ index a71759adcb7363..5ede9a29a82750 100644 FIPS, internal/godebug, embed, crypto/internal/boring/sig, crypto/internal/boring/syso, -@@ -584,7 +602,8 @@ var depsRules = ` +@@ -584,7 +601,8 @@ var depsRules = ` crypto/internal/fips140only, crypto, crypto/subtle, @@ -8138,7 +8140,7 @@ index a71759adcb7363..5ede9a29a82750 100644 < crypto/sha3 < crypto/internal/fips140hash < crypto/internal/boring -@@ -603,6 +622,7 @@ var depsRules = ` +@@ -603,6 +621,7 @@ var depsRules = ` crypto/ecdh, crypto/mlkem, crypto/mldsa From 0bab2e24205848b042d46f2a6c9ba263f767d2db Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 3 Jun 2026 12:23:23 +0200 Subject: [PATCH 31/36] Fix FIPS backend patch hunk count --- patches/0002-Add-crypto-backends.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/0002-Add-crypto-backends.patch b/patches/0002-Add-crypto-backends.patch index 23c70418e4..89ff445550 100644 --- a/patches/0002-Add-crypto-backends.patch +++ b/patches/0002-Add-crypto-backends.patch @@ -4275,7 +4275,7 @@ new file mode 100644 index 00000000000000..436cb00f359bcf --- /dev/null +++ b/src/crypto/internal/backend/fips140/fips140.go -@@ -0,0 +1,12 @@ +@@ -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. From 3c48f15f29d0e49f4715bb771a741b984043375e Mon Sep 17 00:00:00 2001 From: George Adams Date: Wed, 3 Jun 2026 13:26:14 +0100 Subject: [PATCH 32/36] Add copilot instructions for crypto backend implementation (#2344) --- .github/copilot-instructions.md | 240 ++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..a1e57e9b1c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,240 @@ +# crypto-backend + +Use when implementing, modifying, or adding crypto algorithms to the Microsoft 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 + +Microsoft 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 From 48d8eaf984f112f11cd0039a5ca5d284dc6fb61e Mon Sep 17 00:00:00 2001 From: "bot-for-go[bot]" <199222863+bot-for-go[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:28:01 +0000 Subject: [PATCH 33/36] Update submodule to latest master (0252b4be): runtime: format generic methods properly in tracebacks --- go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go b/go index a81b20a431..0252b4be03 160000 --- a/go +++ b/go @@ -1 +1 @@ -Subproject commit a81b20a431ce41417f7758929a2962d95a6a9453 +Subproject commit 0252b4be0300559f6ff0032636d84aa0acb71e83 From 8d8536238ff8c3eadd847410e7a955f027097e11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 23:51:58 +0000 Subject: [PATCH 34/36] build(deps): bump github/codeql-action from 4.36.0 to 4.36.1 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.0 to 4.36.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/7211b7c8077ea37d8641b6271f6a365a22a5fbfa...87557b9c84dde89fdd9b10e88954ac2f4248e463) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.36.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 334cc5409f..a66c111e2b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,16 +23,16 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Initialize CodeQL - uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/autobuild@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 with: working-directory: eng/_util - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/analyze@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 with: category: /language:go From abfddd92620f8b89859f1c733189b62184140e84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 23:52:03 +0000 Subject: [PATCH 35/36] build(deps): bump actions/checkout from 6.0.2 to 6.0.3 Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 2 +- .github/workflows/linter.yml | 2 +- .github/workflows/patch-build.yml | 4 ++-- .github/workflows/test.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 334cc5409f..5914f4347c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,7 +20,7 @@ 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@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 3a5bbf562b..9cfaba96e1 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 4c947c0a94..df022fd23c 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 7682d7f301..d68a61c7f4 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 From 1057e04edc777d8890333016446800aee7604db0 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 4 Jun 2026 16:36:47 -0700 Subject: [PATCH 36/36] Fix references to Microsoft build of Go (#2349) --- .github/copilot-instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a1e57e9b1c..8f9b7fdf56 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,6 +1,6 @@ # crypto-backend -Use when implementing, modifying, or adding crypto algorithms to the Microsoft 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. +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 @@ -8,7 +8,7 @@ See [eng/doc/DeveloperGuide.md](/eng/doc/DeveloperGuide.md) for the full develop ## Architecture overview -Microsoft Go replaces upstream BoringCrypto (`crypto/internal/boring`) with a pluggable backend system (`crypto/internal/backend`). The backend dispatches crypto operations to platform-native libraries: +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`