golang/go · error

mldsa: invalid public key length

Error message

mldsa: invalid public key length

What it means

An ML-DSA public key is a fixed-size blob whose length is determined by the parameter set: 1312 bytes for ML-DSA-44, 1952 for ML-DSA-65, 2592 for ML-DSA-87. NewPublicKey44/65/87 delegate to newPublicKey, which rejects any slice that does not match the expected size for that variant. The check happens before bit-unpacking, so a truncated or wrong-variant blob is treated as malformed rather than as a verification problem.

Source

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

	if len(pk) != pubKeySize(p) {
		return nil, errInvalidPublicKeyLength
	}
	ρ, pk = pk[:32], pk[32:]
	for r := range t1 {
		// Decode four at a time from 4 * 10 bits = 5 bytes.
		for i := 0; i < n; i += 4 {
			b0, b1, b2, b3, b4 := pk[0], pk[1], pk[2], pk[3], pk[4]
			t1[r][i+0] = uint16(b0>>0) | uint16(b1&0b0000_0011)<<8
			t1[r][i+1] = uint16(b1>>2) | uint16(b2&0b0000_1111)<<6
			t1[r][i+2] = uint16(b2>>4) | uint16(b3&0b0011_1111)<<4
			t1[r][i+3] = uint16(b3>>6) | uint16(b4&0b1111_1111)<<2
			pk = pk[5:]
		}
	}
	return ρ, nil
}

var errInvalidPublicKeyLength = errors.New("mldsa: invalid public key length")

func NewPublicKey44(pk []byte) (*PublicKey, error) {
	return newPublicKey(&PublicKey{}, pk, params44)
}

func NewPublicKey65(pk []byte) (*PublicKey, error) {
	return newPublicKey(&PublicKey{}, pk, params65)
}

func NewPublicKey87(pk []byte) (*PublicKey, error) {
	return newPublicKey(&PublicKey{}, pk, params87)
}

func newPublicKey(pub *PublicKey, pk []byte, p parameters) (*PublicKey, error) {
	if len(pk) != pubKeySize(p) {
		return nil, errInvalidPublicKeyLength
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Store the parameter set alongside the raw bytes and dispatch to the matching NewPublicKey44/65/87 constructor.
  2. Strip any PEM/base64/hex encoding before passing the slice; assert len(pk) equals the expected variant size first.
  3. If the key came from encoding x509/ssl, use the package's PublicKey parsing helpers rather than feeding DER directly.
  4. Round-trip test serialization (Bytes() -> NewPublicKey*) in unit tests to catch envelope bugs early.

Example fix

// before
pub, err := mldsa.NewPublicKey65(pemBytes)   // PEM still encoded

// after
block, _ := pem.Decode(pemBytes)
spki, err := x509.ParsePKIXPublicKey(block.Bytes)
// then convert via the mldsa helpers, or
pub, err := mldsa.NewPublicKey65(raw1952Bytes)
Defensive patterns

Strategy: validation

Validate before calling

want := mldsa.PublicKeySizeForVariant(v) // 1312/1952/2592
if len(pk) != want {
    return fmt.Errorf("public key must be %d bytes, got %d", want, len(pk))
}

Type guard

func isMLDSA44PublicKey(pk []byte) bool { return len(pk) == 1312 }

Prevention

When it happens

Trigger: Calling mldsa.NewPublicKey44/65/87 with a []byte whose length is not the variant's PublicSize; e.g. feeding a 1952-byte ML-DSA-65 key into NewPublicKey44.

Common situations: Persisting/transmitting a key without recording its parameter set and reconstructing the wrong variant on load; PEM/base64/hex envelope not stripped; accidental truncation in a length-prefixed protocol; loading a DER/SPKI key without first extracting the raw ML-DSA seed.

Related errors


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