Tencent/WeKnora · error

plaintext too short

Error message

plaintext too short

What it means

WeCom plaintext has the layout random(16 bytes) + msg_len(4 bytes, big-endian) + msg + corp_id, so at least 20 bytes are required before the message-length field can even be read. decrypt() returns this error when the unpadded plaintext is shorter than 20 bytes. It almost always accompanies a key mismatch that yielded garbage padding, or a genuinely truncated payload.

Source

Thrown at internal/im/wecom/webhook_adapter.go:484

	iv := a.aesKey[:aes.BlockSize]
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(ciphertext, ciphertext)

	// Remove and verify PKCS#7 padding
	padLen := int(ciphertext[len(ciphertext)-1])
	if padLen > wecomPKCS7BlockSize || padLen == 0 || padLen > len(ciphertext) {
		return nil, fmt.Errorf("invalid padding")
	}
	for i := 0; i < padLen; i++ {
		if ciphertext[len(ciphertext)-1-i] != byte(padLen) {
			return nil, fmt.Errorf("invalid padding")
		}
	}
	plaintext := ciphertext[:len(ciphertext)-padLen]

	// WeCom format: random(16) + msg_len(4) + msg + corp_id
	if len(plaintext) < 20 {
		return nil, fmt.Errorf("plaintext too short")
	}

	msgLen := binary.BigEndian.Uint32(plaintext[16:20])
	if uint32(len(plaintext)) < 20+msgLen {
		return nil, fmt.Errorf("message length mismatch")
	}

	msgBytes := plaintext[20 : 20+msgLen]

	// Verify corp_id from plaintext tail
	corpIDBytes := plaintext[20+msgLen:]
	if string(corpIDBytes) != a.corpID {
		return nil, fmt.Errorf("corp_id mismatch: expected %s, got %s", a.corpID, string(corpIDBytes))
	}

	return msgBytes, nil
}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Fix the underlying key mismatch first — a correct key on real WeCom data always yields >= 20 bytes of plaintext
  2. Check for truncation of the Encrypt field upstream (proxies, logs, form parsing) and pass the raw body bytes
  3. Validate the decrypted length sanity in tests by encrypting a sample via the official WeCom crypto sample
  4. Confirm the base64 decode uses StdEncoding, not URL-safe variants
Defensive patterns

Strategy: try-catch

Type guard

func hasWeComPlaintextLayout(plaintext []byte) bool { return len(plaintext) >= 20 }

Try / catch

msg, err := adapter.ParseCallback(sig, ts, nonce, enc)
if err != nil && strings.Contains(err.Error(), "plaintext too short") {
    // almost always a key mismatch; treat as bad request
    http.Error(w, "bad payload", http.StatusBadRequest)
    return
}

Prevention

When it happens

Trigger: PKCS#7 padding with padLen close to 16 stripping nearly all bytes from a short/garbage plaintext; decrypting non-WeCom data that happens to pass padding checks; severely truncated callback payloads.

Common situations: Wrong EncodingAESKey producing valid-looking-but-garbage padding; a test posting tiny payloads; intermediary systems compressing or transforming the encrypted body.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/85252e2592f13222. Report an issue: GitHub.