golang/go · error

ciphertext too short

Error message

ciphertext too short

What it means

The package-level convenience Open() expects ciphertext to be the concatenation of the KEM encapsulated key and the AEAD ciphertext. It reads the first encSize bytes (k.KEM().encSize()) as the encapsulated key; if the input is shorter than encSize, there is no encapsulated key to decapsulate and the call fails fast.

Source

Thrown at src/crypto/hpke/hpke.go:227

func (r *Recipient) Open(aad, ciphertext []byte) ([]byte, error) {
	if r.aead == nil {
		return nil, errors.New("export-only instantiation")
	}
	plaintext, err := r.aead.Open(nil, r.nextNonce(), ciphertext, aad)
	if err != nil {
		return nil, err
	}
	r.seqNum++
	return plaintext, nil
}

// Open instantiates a single-use HPKE receiving HPKE context like [NewRecipient],
// and then decrypts the provided ciphertext like [Recipient.Open] (with no aad).
// ciphertext must be the concatenation of the encapsulated key and the actual ciphertext.
func Open(k PrivateKey, kdf KDF, aead AEAD, info, ciphertext []byte) ([]byte, error) {
	encSize := k.KEM().encSize()
	if len(ciphertext) < encSize {
		return nil, errors.New("ciphertext too short")
	}
	enc, ciphertext := ciphertext[:encSize], ciphertext[encSize:]
	r, err := NewRecipient(enc, k, kdf, aead, info)
	if err != nil {
		return nil, err
	}
	return r.Open(nil, ciphertext)
}

// Export produces a secret value derived from the shared key between sender and
// recipient. length must be at most 65,535.
func (r *Recipient) Export(exporterContext string, length int) ([]byte, error) {
	if length < 0 || length > 0xFFFF {
		return nil, errors.New("invalid length")
	}
	return r.export(exporterContext, uint16(length))
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Pass the full enc||ciphertext blob exactly as returned by hpke.Seal.
  2. Ensure sender and recipient use the same KEM so encSize matches.
  3. Check len(ciphertext) >= k.KEM().encSize() before calling Open and surface a clearer framing error if not.

Example fix

// before
blob := []byte{} // accidentally stripped
pt, err := hpke.Open(k, kdf, aead, info, blob) // "ciphertext too short"

// after
// blob is enc||ct straight from hpke.Seal
pt, err := hpke.Open(k, kdf, aead, info, blob)
Defensive patterns

Strategy: validation

Validate before calling

func openChecked(k hpke.PrivateKey, kdf hpke.KDF, aead hpke.AEAD, info, blob []byte) ([]byte, error) {
    if min := k.KEM().encSize(); len(blob) < min {
        return nil, fmt.Errorf("blob too short: got %d, need >= %d", len(blob), min)
    }
    return hpke.Open(k, kdf, aead, info, blob)
}

Try / catch

pt, err := hpke.Open(k, kdf, aead, info, blob)
if err != nil {
    if err.Error() == "ciphertext too short" {
        return nil, fmt.Errorf("framing error: need enc(%d)+ct, got %d", k.KEM().encSize(), len(blob))
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling hpke.Open(k, kdf, aead, info, ciphertext) where len(ciphertext) < k.KEM().encSize(). Happens when the caller passes only the AEAD ciphertext (omitting the enc prefix), passes an empty slice, or uses a different KEM whose encSize is larger than the data.

Common situations: Sender used the convenience Seal() (which returns enc||ct) but receiver strips the enc prefix before calling Open(); or the framing on the wire truncated the message; or sender/recipient were provisioned with different KEMs.

Related errors


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