golang/go · error

invalid public key size

Error message

invalid public key size

What it means

hybridKEM.NewPublicKey deserializes a combined hybrid public key blob (PQ encapsulation key || ECDH point). The total length must equal kem.pqEncapsKeySize + kem.curvePointSize, fixed by the chosen combiner. Any other length is rejected before any parsing.

Source

Thrown at src/crypto/hpke/pq.go:166

		return &hybridPublicKey{mlkem768X25519, t, pq}, nil
	case ecdh.P256():
		if _, ok := pq.(*mlkem.EncapsulationKey768); !ok {
			return nil, errors.New("invalid PQ KEM for P-256 hybrid")
		}
		return &hybridPublicKey{mlkem768P256, t, pq}, nil
	case ecdh.P384():
		if _, ok := pq.(*mlkem.EncapsulationKey1024); !ok {
			return nil, errors.New("invalid PQ KEM for P-384 hybrid")
		}
		return &hybridPublicKey{mlkem1024P384, t, pq}, nil
	default:
		return nil, errors.New("unsupported curve")
	}
}

func (kem *hybridKEM) NewPublicKey(data []byte) (PublicKey, error) {
	if len(data) != kem.pqEncapsKeySize+kem.curvePointSize {
		return nil, errors.New("invalid public key size")
	}
	pq, err := kem.pqNewPublicKey(data[:kem.pqEncapsKeySize])
	if err != nil {
		return nil, err
	}
	var k *ecdh.PublicKey
	fips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.
		k, err = kem.curve.NewPublicKey(data[kem.pqEncapsKeySize:])
	})
	if err != nil {
		return nil, err
	}
	return NewHybridPublicKey(pq, k)
}

func (pk *hybridPublicKey) KEM() KEM {
	return pk.kem
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Ensure the blob is exactly pqEncapsKeySize + curvePointSize bytes for the chosen combiner.
  2. Match sender and recipient KEM (e.g. both MLKEM768X25519).
  3. Verify length on the wire before invoking NewPublicKey and emit a clearer framing error.

Example fix

// before
// data was truncated in transit
pub, err := hpke.MLKEM768X25519().NewPublicKey(data) // "invalid public key size"

// after
const want = 1184 + 32 // ML-KEM-768 encapsulation key + X25519 point
if len(data) != want {
    return fmt.Errorf("hybrid pubkey: got %d want %d", len(data), want)
}
pub, err := hpke.MLKEM768X25519().NewPublicKey(data)
Defensive patterns

Strategy: validation

Validate before calling

var hybridKeyLen = map[uint16]int{
    0x647A: 1184 + 32, // MLKEM768-X25519
    0x0050: 1184 + 65, // MLKEM768-P256
    0x0051: 1568 + 97, // MLKEM1024-P384
}
func parseHybridPub(kemID uint16, data []byte) (hpke.PublicKey, error) {
    want, ok := hybridKeyLen[kemID]
    if !ok {
        return nil, fmt.Errorf("unknown hybrid kem 0x%04x", kemID)
    }
    if len(data) != want {
        return nil, fmt.Errorf("hybrid pubkey length: got %d want %d", len(data), want)
    }
    return hpke.NewKEM(kemID).NewPublicKey(data)
}

Try / catch

pub, err := kem.NewPublicKey(data)
if err != nil && err.Error() == "invalid public key size" {
    return nil, fmt.Errorf("framing mismatch: blob %d bytes does not match combiner layout", len(data))
}

Prevention

When it happens

Trigger: Calling MLKEM768X25519().NewPublicKey(data) (or the P256/P384 equivalents) with data whose length does not match the combiner's fixed sum. Triggered by truncation, trailing bytes, or mixing key formats from a different combiner.

Common situations: Sender used a different hybrid KEM than receiver; raw bytes were padded/stripped in transit; misreading the spec on byte layout.

Related errors


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