golang/go · error

crypto/rsa: invalid PSS salt length

Error message

crypto/rsa: invalid PSS salt length

What it means

Thrown by SignPSS when opts.SaltLength resolves to a value <= 0 that is neither PSSSaltLengthAuto (0) nor PSSSaltLengthEqualsHash (-1) — i.e. a negative number below -1, or it slipped past those two cases. The switch handles the two sentinels; the default branch rejects any remaining non-positive length. This is a general input validation, NOT FIPS-specific (it fires regardless of enforcement).

Source

Thrown at src/crypto/rsa/fips.go:121

	}

	saltLength := opts.saltLength()
	if fips140only.Enforced() && saltLength > h.Size() {
		return nil, errors.New("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode")
	}
	switch saltLength {
	case PSSSaltLengthAuto:
		saltLength, err = rsa.PSSMaxSaltLength(k.PublicKey(), h)
		if err != nil {
			return nil, fipsError(err)
		}
	case PSSSaltLengthEqualsHash:
		saltLength = h.Size()
	default:
		// If we get here saltLength is either > 0 or < -1, in the
		// latter case we fail out.
		if saltLength <= 0 {
			return nil, errors.New("crypto/rsa: invalid PSS salt length")
		}
	}

	return fipsError2(rsa.SignPSS(random, k, h, digest, saltLength))
}

// VerifyPSS verifies a PSS signature.
//
// A valid signature is indicated by returning a nil error. digest must be the
// result of hashing the input message using the given hash function. The opts
// argument may be nil, in which case sensible defaults are used. opts.Hash is
// ignored.
//
// The inputs are not considered confidential, and may leak through timing side
// channels, or if an attacker has control of part of the inputs.
func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *PSSOptions) error {
	if err := checkPublicKeySize(pub); err != nil {
		return err

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Use rsa.PSSSaltLengthEqualsHash (-1) or rsa.PSSSaltLengthAuto (0) instead of custom negative values.
  2. If you need an explicit length, ensure it is a positive integer greater than zero.
  3. Validate opts.SaltLength before calling SignPSS if the value comes from untrusted/config input.

Example fix

// before
opts := &rsa.PSSOptions{SaltLength: configSalt /* accidentally -2 */}
sig, err := rsa.SignPSS(rand.Reader, priv, crypto.SHA256, digest, opts)

// after
if opts.SaltLength < -1 || (opts.SaltLength < 0 && opts.SaltLength != rsa.PSSSaltLengthEqualsHash) {
    opts.SaltLength = rsa.PSSSaltLengthEqualsHash
}
sig, err := rsa.SignPSS(rand.Reader, priv, crypto.SHA256, digest, opts)
Defensive patterns

Strategy: validation

Validate before calling

func validatePSSSaltLength(opts *rsa.PSSOptions) error {
    if opts == nil {
        return nil
    }
    sl := opts.SaltLength
    if sl == rsa.PSSSaltLengthAuto || sl == rsa.PSSSaltLengthEqualsHash {
        return nil
    }
    if sl <= 0 {
        return fmt.Errorf("invalid PSS salt length %d: use PSSSaltLengthAuto, PSSSaltLengthEqualsHash, or a positive int", sl)
    }
    return nil
}
if err := validatePSSSaltLength(opts); err != nil { return err }

Type guard

func isValidSaltLength(sl int) bool {
    return sl == rsa.PSSSaltLengthAuto ||
        sl == rsa.PSSSaltLengthEqualsHash ||
        sl > 0
}

Prevention

When it happens

Trigger: Passing &rsa.PSSOptions{SaltLength: -2} (or any value < -1) to SignPSS. A zero-value PSSOptions does NOT trip this because SaltLength 0 == PSSSaltLengthAuto which is handled. An uninitialized *int from a config struct that defaults to a negative sentinel accidentally can trip it.

Common situations: Config file maps a missing salt-length field to -1 as 'not set' but -1 is actually PSSSaltLengthEqualsHash; a separate 'disabled' code path uses -2 and hits validation. Off-by-one arithmetic on salt length producing a negative result.

Related errors


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