Skip to content

x/crypto/ssh: panic stating scalarBaseMult was not 32 bytes on FIPS enabled host with curve25519 #75061

@anandf

Description

@anandf

Go version

go version go1.24.4 (Red Hat 1.24.4-1.module+el8.10.0+23323+67916f33) linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/opt/app-root/src/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/opt/app-root/src/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2709008956=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/opt/app-root/src/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/opt/app-root/src/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/golang'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/opt/app-root/src/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/lib/golang/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.4 (Red Hat 1.24.4-1.module+el8.10.0+23323+67916f33)'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

Compiled the below source code for FIPS compliance as

RUN CGO_ENABLED=1 GOOS=linux GOEXPERIMENT=strictfipsruntime go build -tags strictfipsruntime -buildvcs=false -o github-ssh-client .

Full source code and Dockerfile available at https://github.com/anandf/ssh-fips

package main

import (
	"crypto/fips140"
	"log"
	"os"
	"path/filepath"
)

import (
	"golang.org/x/crypto/ssh"
)

func main() {
	log.Printf("Fips enabled? %v\n", fips140.Enabled())
	homeDir, err := os.UserHomeDir()
	if err != nil {
		log.Fatalf("error when getting user's home directory: %v", err)
	}
	sshKeyPath := filepath.Join(homeDir, ".ssh", "id_rsa")
	sshKey, err := os.ReadFile(sshKeyPath)
	if err != nil {
		log.Fatalf("error when reading private key from %s: %v", sshKeyPath, err)
	}
	sshKeySigner, err := ssh.ParsePrivateKey(sshKey)
	if err != nil {
		log.Fatalf("error when parsing private key: %v", err)
	}
	cfg := &ssh.ClientConfig{
		User:            "git",
		Auth:            []ssh.AuthMethod{ssh.PublicKeys(sshKeySigner)},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	cfg.SetDefaults()

	//cfg.Ciphers = []string{"[email protected]"}
	cfg.KeyExchanges = []string{"curve25519-sha256"}
	conn, err := ssh.Dial("tcp", "github.com:22", cfg)
	if err != nil {
		log.Fatalf("Failed to dial: %v", err)
	}
	defer conn.Close()
	log.Println("SSH connection was successful")
}

What did you see happen?

When running in a cluster in FIPS enabled mode, the program crashes with the below panic and stack trace.

2025/08/18 07:15:22 Fips enabled? false
panic: curve25519: internal error: scalarBaseMult was not 32 bytes

goroutine 16 [running]:
golang.org/x/crypto/curve25519.ScalarBaseMult(0xc0001343a0, 0x6c2a50?)
        /opt/app-root/src/go/pkg/mod/golang.org/x/[email protected]/curve25519/curve25519.go:39 +0x13b
golang.org/x/crypto/ssh.(*curve25519KeyPair).generate(0xc000134380, {0x6c4dc0?, 0x6c2a50?})
        /opt/app-root/src/go/pkg/mod/golang.org/x/[email protected]/ssh/kex.go:457 +0x65
golang.org/x/crypto/ssh.(*curve25519sha256).Client(0xa30740?, {0x7f739b2feb18, 0xc000116120}, {0x6c4dc0, 0x6c2a50}, 0xc00006a540)
        /opt/app-root/src/go/pkg/mod/golang.org/x/[email protected]/ssh/kex.go:468 +0x6e
golang.org/x/crypto/ssh.(*handshakeTransport).client(0xc0001084e0, {0x6c5208?, 0xa79720?}, 0xc00006a540?)
        /opt/app-root/src/go/pkg/mod/golang.org/x/[email protected]/ssh/handshake.go:826 +0x66
golang.org/x/crypto/ssh.(*handshakeTransport).enterKeyExchange(0xc0001084e0, {0xc0000dc300, 0x2d8, 0x2d8})
        /opt/app-root/src/go/pkg/mod/golang.org/x/[email protected]/ssh/handshake.go:716 +0x49a
golang.org/x/crypto/ssh.(*handshakeTransport).kexLoop(0xc0001084e0)
        /opt/app-root/src/go/pkg/mod/golang.org/x/[email protected]/ssh/handshake.go:345 +0x98
created by golang.org/x/crypto/ssh.newClientTransport in goroutine 1
        /opt/app-root/src/go/pkg/mod/golang.org/x/[email protected]/ssh/handshake.go:170 +0x256

What did you expect to see?

The key exchange algorithm curve25519 is not FIPS compliant. There should be graceful error handling that says that the selected algorithim is not FIPS compliant and if there are other available FIPS compliant algorithims available in the list, those should be prioritized for the key exchange.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions