golang/go · error

mldsa: invalid seed length

Error message

mldsa: invalid seed length

What it means

ML-DSA (FIPS 204) key generation derives the full keypair deterministically from a 32-byte seed ξ. The constructors NewPrivateKey44/65/87 validate that the supplied seed slice is exactly 32 bytes before invoking the (expensive) key-derivation routine, refusing to run on malformed input. A wrong length therefore never reaches FIPS self-tests and is reported as a plain input error rather than a cryptographic failure.

Source

Thrown at src/crypto/internal/fips140/mldsa/mldsa.go:143

	fips140.RecordApproved()
	var seed [32]byte
	drbg.Read(seed[:])
	priv := newPrivateKey(&seed, params65)
	fipsPCT(priv)
	return priv
}

func GenerateKey87() *PrivateKey {
	fipsSelfTest()
	fips140.RecordApproved()
	var seed [32]byte
	drbg.Read(seed[:])
	priv := newPrivateKey(&seed, params87)
	fipsPCT(priv)
	return priv
}

var errInvalidSeedLength = errors.New("mldsa: invalid seed length")

func NewPrivateKey44(seed []byte) (*PrivateKey, error) {
	fipsSelfTest()
	fips140.RecordApproved()
	if len(seed) != 32 {
		return nil, errInvalidSeedLength
	}
	return newPrivateKey((*[32]byte)(seed), params44), nil
}

func NewPrivateKey65(seed []byte) (*PrivateKey, error) {
	fipsSelfTest()
	fips140.RecordApproved()
	if len(seed) != 32 {
		return nil, errInvalidSeedLength
	}
	return newPrivateKey((*[32]byte)(seed), params65), nil
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Allocate a [32]byte and fill it with crypto/rand.Read(seed[:]) before calling NewPrivateKey*.
  2. If the seed arrives hex/base64-encoded, decode it first (hex.DecodeString / base64.StdEncoding.DecodeString) and assert the decoded length is 32.
  3. If you do not need deterministic key derivation from a seed, call GenerateKey44/65/87 instead, which internally draws the 32 bytes from the FIPS DRBG.
  4. Add a length guard at the trust boundary (API handler, deserializer) so malformed seeds are rejected before reaching the crypto layer.

Example fix

// before
seed := []byte("my-fixed-passphrase-seed")   // wrong length
pk, err := mldsa.NewPrivateKey44(seed)

// after
var seed [32]byte
if _, err := io.ReadFull(rand.Reader, seed[:]); err != nil { return err }
pk, err := mldsa.NewPrivateKey44(seed[:])
Defensive patterns

Strategy: validation

Validate before calling

if len(seed) != 32 {
    return fmt.Errorf("mldsa seed must be 32 bytes, got %d", len(seed))
}
pk, err := mldsa.NewPrivateKey44(seed)

Type guard

// seed must be exactly 32 bytes; represent it as an array to make the
// length a compile-time property.
func validateSeed(seed *[32]byte) bool { return seed != nil }

Prevention

When it happens

Trigger: Calling mldsa.NewPrivateKey44(seed), NewPrivateKey65(seed), or NewPrivateKey87(seed) with a []byte whose length is not 32 (e.g. 16, 48, 64). The len(seed) != 32 branch at the top of the constructor returns errInvalidSeedLength.

Common situations: Passing a hex- or base64-encoded string instead of raw bytes (length 64/44 instead of 32); reading a short/truncated byte slice from a config file or env var; confusing ML-DSA's 32-byte seed with ML-KEM's 64-byte d||z seed; using crypto/rand with a wrong-sized buffer.

Related errors


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