JuliusBrussee/caveman · error

ciphertext too short

Error message

ciphertext too short

What it means

Decrypt's local path found the envelope shorter than the GCM nonce size (12 bytes) — there is no nonce to split off, so the ciphertext is truncated, corrupt, or simply not a secretbox envelope. This length guard runs before gcm.Open, which would otherwise panic or misparse.

Source

Thrown at shared/platform/secretbox/secretbox.go:132

	if runtimeenv.IsProduction() &&
		!strings.EqualFold(strings.TrimSpace(os.Getenv("CAVE_KMS_ALLOW_LEGACY_LOCAL_DECRYPT")), "true") {
		return nil, fmt.Errorf("secretbox: production refuses legacy local ciphertext")
	}
	keyBytes, err := loadKey()
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		return nil, fmt.Errorf("aes cipher: %w", err)
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, fmt.Errorf("aes-gcm: %w", err)
	}
	ns := gcm.NonceSize()
	if len(envelope) < ns {
		return nil, fmt.Errorf("ciphertext too short")
	}
	nonce, ct := envelope[:ns], envelope[ns:]
	plain, err := gcm.Open(nil, nonce, ct, nil)
	if err != nil {
		return nil, fmt.Errorf("aes-gcm open: %w", err)
	}
	return plain, nil
}

// DecryptPayloadKey unwraps an artifact data-encryption key. KMS envelopes are
// restricted to the configured payload key plus the explicit legacy secrets
// key used before key separation.
func DecryptPayloadKey(envelope []byte) ([]byte, error) {
	if kms.IsEnvelope(envelope) {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		plaintext, err := kms.DecryptPayload(ctx, envelope)
		if err != nil {

View on GitHub (pinned to 766dce6b13)

Solutions

  1. Check how the envelope was transported/stored — truncation usually happens in a VARCHAR column, log field, or copy-paste
  2. Validate envelope length before storing and use bytea/base64 transport end to end
  3. If the value is actually plaintext or another format, route it through the correct decode path
  4. Discard unrecoverable fragments; there is no way to decrypt a truncated GCM ciphertext
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at shared/platform/secretbox/secretbox.go:132 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/1fe530d43b22a78f. Report an issue: GitHub.