golang/go · error

crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140

Error message

crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140-only mode

What it means

Thrown in ed25519.go:116 during PrivateKey.Sign when Ed25519ctx is selected (Hash==0 && Context != "") and fips140only.Enforced() is true. Ed25519ctx (pre-hash context variant) is not an approved FIPS 140 algorithm, so it is blocked in FIPS 140-only mode.

Source

Thrown at src/crypto/ed25519/ed25519.go:116

	k, err := privateKeyCache.Get(&priv[0], func() (*ed25519.PrivateKey, error) {
		return ed25519.NewPrivateKey(priv)
	}, func(k *ed25519.PrivateKey) bool {
		return subtle.ConstantTimeCompare(priv, k.Bytes()) == 1
	})
	if err != nil {
		return nil, err
	}
	hash := opts.HashFunc()
	context := ""
	if opts, ok := opts.(*Options); ok {
		context = opts.Context
	}
	switch {
	case hash == crypto.SHA512: // Ed25519ph
		return ed25519.SignPH(k, message, context)
	case hash == crypto.Hash(0) && context != "": // Ed25519ctx
		if fips140only.Enforced() {
			return nil, errors.New("crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140-only mode")
		}
		return ed25519.SignCtx(k, message, context)
	case hash == crypto.Hash(0): // Ed25519
		return ed25519.Sign(k, message), nil
	default:
		return nil, errors.New("ed25519: expected opts.HashFunc() zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)")
	}
}

// Options can be used with [PrivateKey.Sign] or [VerifyWithOptions]
// to select Ed25519 variants.
type Options struct {
	// Hash can be zero for regular Ed25519, or crypto.SHA512 for Ed25519ph.
	Hash crypto.Hash

	// Context, if not empty, selects Ed25519ctx or provides the context string
	// for Ed25519ph. It can be at most 255 bytes in length.
	Context string

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Drop the Context for plain Ed25519 (Options{Hash: crypto.Hash(0), Context: ""}), or use Ed25519ph (Hash: crypto.SHA512) which is FIPS-approved.
  2. Disable FIPS 140-only mode if Ed25519ctx is a protocol requirement and FIPS compliance is not.
  3. Review the signer's Options to confirm Context is empty under FIPS builds.

Example fix

// before
opts := ed25519.Options{Context: "app-v1"}
sig, err := priv.Sign(rand.Reader, msg, &opts) // FIPS-only -> error 248

// after
sig, err := priv.Sign(rand.Reader, msg, crypto.Hash(0)) // plain Ed25519
Defensive patterns

Strategy: validation

Validate before calling

if opts, ok := signOpts.(*ed25519.Options); ok && opts.Context != "" && fips140only.Enforced() {
    return errors.New("Ed25519ctx not allowed in FIPS 140-only mode")
}

Type guard

func isEd25519ctx(o *ed25519.Options) bool {
    return o != nil && o.Hash == crypto.Hash(0) && o.Context != ""
}

Prevention

When it happens

Trigger: Calling priv.Sign with ed25519.Options{Context: "myctx"} (non-empty Context, default Hash) while FIPS 140-only is active. This routes to the Ed25519ctx branch and hits the guard.

Common situations: FIPS-validated build using context-separated Ed25519 (common in some protocols/age-style tools); a library defaulting to a context string; enabling GOFIPS/fips140 build tag.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/d7ad815d164afb0d. Report an issue: GitHub.