fish2018/pansou · error

ciphertext too short

Error message

ciphertext too short

What it means

The AES-GCM decryption helper (decrypt, near plugin/qqpd/qqpd.go:2350) validates that the supplied ciphertext is at least as long as the GCM nonce size (12 bytes) before attempting gcm.Open. A shorter input can never be valid GCM output, so it fails fast with "ciphertext too short" instead of a confusing crypto error.

Solutions

  1. Verify the input being decrypted is the exact encrypted output (base64 of nonce+ciphertext), not plaintext or a URL-decoded fragment.
  2. Check for accidental encoding round-trips (e.g. base64 decoded twice) that shorten the payload.
  3. Re-generate/re-store the encrypted credential with the same encrypt helper.
  4. Confirm you are decrypting the right field — a wrong config key often yields an empty or short value.

Example fix

// before
data, _ := base64.StdEncoding.DecodeString(input) // error ignored
cleartext, err := decrypt(key, data)
// after
data, err := base64.StdEncoding.DecodeString(input)
if err != nil {
    return "", fmt.Errorf("bad ciphertext encoding: %w", err)
}
cleartext, err := decrypt(key, data)
if err != nil && strings.Contains(err.Error(), "ciphertext too short") {
    return "", fmt.Errorf("credential looks unencrypted or truncated; re-save it")
}
Defensive patterns

Strategy: validation

Validate before calling

decoded, err := base64.StdEncoding.DecodeString(ciphertextB64)
if err != nil || len(decoded) < 12 {
    return fmt.Errorf("credential is not valid encrypted data (len=%d)", len(decoded))
}

Type guard

func looksEncrypted(s string) bool {
    b, err := base64.StdEncoding.DecodeString(s)
    return err == nil && len(b) >= 12
}

Try / catch

plain, err := decrypt(key, data)
if err != nil {
    if strings.Contains(err.Error(), "ciphertext too short") {
        // value was stored unencrypted or truncated — prompt re-save
    } else {
        // wrong key or corrupted data
    }
}

Prevention

When it happens

Trigger: Passing an empty string/byte slice, a plaintext value, or a truncated base64 payload to the decrypt function — anything shorter than gcm.NonceSize() (12) bytes after decoding.

Common situations: Config/cookie field stored unencrypted or re-encoded (e.g. double base64 decode stripping bytes); credential file truncated or partially written; wrong field passed to decrypt.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/6cfe458e45d2c003. Report an issue: GitHub.

Appendix: source

Thrown at plugin/qqpd/qqpd.go:2350

	ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

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

	nonceSize := gcm.NonceSize()
	if len(ciphertext) < nonceSize {
		return "", fmt.Errorf("ciphertext too short")
	}

	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return "", err
	}

	return string(plaintext), nil
}

// ============ 定期清理 ============

// startCleanupTask 定期清理任务
func (p *QQPDPlugin) startCleanupTask() {
	ticker := time.NewTicker(24 * time.Hour)
	for range ticker.C {
		deleted := p.cleanupExpiredUsers()

View on GitHub (pinned to beaa561337)