ginuerzh/gost · error

ciphertext too short

Error message

ciphertext too short

What it means

decrypt for QUIC packets requires the ciphertext blob to carry at least a GCM nonce prefix; if the received datagram is shorter than gcm.NonceSize(), there is no room for the nonce and authentication cannot proceed, so it fails early with this error.

Solutions

  1. Verify the peer is actually the expected QUIC service using the same key/encryption scheme
  2. Check network path for datagram truncation (MTU, buggy NAT, fragmentation)
  3. Log and drop the offending datagram; a single bad packet doesn't require reconnecting
  4. Confirm both sides use the same cipher configuration (nonce size)

Example fix

// before
n, addr, err := conn.ReadFrom(buf) // assume all data is decryptable
// after
n, addr, err := conn.ReadFrom(buf)
if err != nil { continue }
if n < gcmStandardNonceSize { continue } // drop too-short packets before decrypt
Defensive patterns

Strategy: validation

Validate before calling

if n < gcmStandardNonceSize {
    continue // drop too-short datagram before decrypting
}

Try / catch

data, err := decrypt(buf[:n])
if err != nil {
    log.Log("dropping undecryptable packet:", err)
    continue
}

Prevention

When it happens

Trigger: ReadFrom receives a UDP datagram whose payload length is less than the AES-GCM nonce size after the packet is routed to decrypt — i.e. a truncated, empty, or non-encrypted packet.

Common situations: Random internet UDP noise/scanning hitting the QUIC port, an MTU/truncation issue, or a peer not using the expected encryption.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/10067f3c83160f71. Report an issue: GitHub.

Appendix: source

Thrown at quic.go:333

	}

	return gcm.Seal(nonce, nonce, data, nil), nil
}

func (conn *quicCipherConn) decrypt(data []byte) ([]byte, error) {
	c, err := aes.NewCipher(conn.key)
	if err != nil {
		return nil, err
	}

	gcm, err := cipher.NewGCM(c)
	if err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	if len(data) < nonceSize {
		return nil, errors.New("ciphertext too short")
	}

	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
	return gcm.Open(nil, nonce, ciphertext, nil)
}

func tlsConfigQUICALPN(tlsConfig *tls.Config) *tls.Config {
	if tlsConfig == nil {
		panic("quic: tlsconfig is nil")
	}
	tlsConfigQUIC := tlsConfig.Clone()
	tlsConfigQUIC.NextProtos = []string{"http/3", "quic/v1"}
	return tlsConfigQUIC
}

View on GitHub (pinned to a33fdbf4c9)