chenhg5/cc-connect · error

ciphertext length %d not aligned to block

Error message

ciphertext length %d not aligned to block

What it means

After the key check, decryptAESECB verifies len(ciphertext)%aes.BlockSize==0; AES operates on 16-byte blocks and ECB mode cannot handle a partial final block, so a non-aligned ciphertext returns 'ciphertext length %d not aligned to block'. This almost always indicates an incomplete or corrupted download rather than a padding problem (padding is checked later by pkcs7Unpad).

Source

Thrown at platform/weixin/cdn.go:77

	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	padded := pkcs7Pad(plaintext, aes.BlockSize)
	out := make([]byte, len(padded))
	for i := 0; i < len(padded); i += aes.BlockSize {
		block.Encrypt(out[i:i+aes.BlockSize], padded[i:i+aes.BlockSize])
	}
	return out, nil
}

func decryptAESECB(ciphertext, key []byte) ([]byte, error) {
	if len(key) != 16 {
		return nil, fmt.Errorf("aes key must be 16 bytes, got %d", len(key))
	}
	if len(ciphertext)%aes.BlockSize != 0 {
		return nil, fmt.Errorf("ciphertext length %d not aligned to block", len(ciphertext))
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	out := make([]byte, len(ciphertext))
	for i := 0; i < len(ciphertext); i += aes.BlockSize {
		block.Decrypt(out[i:i+aes.BlockSize], ciphertext[i:i+aes.BlockSize])
	}
	return pkcs7Unpad(out, aes.BlockSize)
}

// parseAesKey decodes CDNMedia.aes_key: base64(raw 16 bytes) or base64(32-char hex ASCII) → 16 bytes.
func parseAesKey(aesKeyBase64, label string) ([]byte, error) {
	decoded, err := base64.StdEncoding.DecodeString(strings.TrimSpace(aesKeyBase64))
	if err != nil {
		return nil, fmt.Errorf("%s: aes_key base64: %w", label, err)
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Re-download the CDN media and verify the byte count matches the expected file size before decrypting
  2. Check HTTP status and Content-Length of the CDN response before ReadAll completes
  3. Ensure the entire response body was read (io.ReadAll, no partial reads/slices)
  4. If the payload is genuinely unaligned, the data is not valid AES-ECB ciphertext — do not attempt decryption

Example fix

data, err := io.ReadAll(resp.Body)
if err != nil { return err }
if resp.StatusCode != 200 || int64(len(data)) != resp.ContentLength {
    return fmt.Errorf("cdn: incomplete download (%d/%d bytes)", len(data), resp.ContentLength)
}
plain, err := decryptAESECB(data, key)
Defensive patterns

Strategy: validation

Validate before calling

if len(data)%aes.BlockSize != 0 {
    return fmt.Errorf("cdn: got %d bytes, not multiple of 16 — download incomplete", len(data))
}
plain, err := decryptAESECB(data, key)

Try / catch

if plain, err := decryptAESECB(data, key); err != nil {
    if strings.Contains(err.Error(), "not aligned to block") {
        return redownloadWithVerification(media) // truncated body
    }
    return err
}

Prevention

When it happens

Trigger: downloadAndDecryptCDN received a truncated body (connection dropped mid-download, Content-Length mismatch); the CDN returned a partial/error response; the caller sliced the ciphertext incorrectly before decrypting; empty ciphertext (len 0 is aligned, so this fires for lengths like 1..15 mod 16).

Common situations: Flaky network during large media download; proxy truncating the response; reading only part of resp.Body; retry logic that appended partial chunks.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/d5673c26ab5600f3. Report an issue: GitHub.