crowdsecurity/crowdsec · critical

failed to create cipher: %w

Error message

failed to create cipher: %w

What it means

After HKDF key derivation, sealCookieV0 creates the AES block cipher via aes.NewCipher. This error means Go's AES implementation rejected the key. With a correct 32-byte HKDF output this is effectively impossible; it indicates the derived key has an invalid length, i.e. a broken keyring or a corrupted deriveKey path.

Source

Thrown at pkg/appsec/challenge/crypto.go:124

//
// Returns ErrAllowlistReasonSize if reason exceeds MaxAllowlistReasonLen.
func sealCookieV0(envelope *pb.ChallengeCookie, masterCookieKey []byte, notAfter int64, flags byte, reason string, aad []byte, maxCookieLen int) (string, error) {
	if maxCookieLen <= 0 {
		maxCookieLen = MaxCookieLen
	}

	if len(reason) > MaxAllowlistReasonLen {
		return "", fmt.Errorf("%w: %d > %d", ErrAllowlistReasonSize, len(reason), MaxAllowlistReasonLen)
	}

	key, err := deriveKey(masterCookieKey)
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("failed to create cipher: %w", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("failed to create GCM: %w", err)
	}

	// Reject an over-limit envelope before marshaling it.
	maxPlaintext := maxCookieLen/4*3 - 1 - gcm.NonceSize() - gcm.Overhead()
	if plaintextLen := cookiePlaintextFixedHeaderLen + len(reason) + proto.Size(envelope); plaintextLen > maxPlaintext {
		return "", fmt.Errorf("%w: plaintext=%d > %d", ErrCookieTooLarge, plaintextLen, maxPlaintext)
	}

	envelopeBytes, err := proto.Marshal(envelope)
	if err != nil {
		return "", fmt.Errorf("failed to marshal challenge cookie proto: %w", err)
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Inspect the wrapped error; if it mentions illegal key size, audit how masterCookieKey reaches sealCookieV0.
  2. Rebuild the ChallengeRuntime through its standard constructor so the keyring produces a proper 32-byte key.
  3. Verify the hkdf.Read call in deriveKey returns no error and fills all 32 bytes.
Defensive patterns

Strategy: try-catch

Try / catch

if err != nil {
    return fmt.Errorf("cookie sealing failed (cipher): %w", err)
}

Prevention

When it happens

Trigger: Calling sealCookieV0 when deriveKey returned a key whose length is not 16/24/32 bytes — only possible with a corrupted or improperly initialized master cookie key, or an hkdf stream truncated by an error path.

Common situations: Programmatic misuse of the challenge package bypassing normal runtime construction; in practice developers rarely see this — it is a defensive check on an internal invariant.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/85035a0b4158dde4. Report an issue: GitHub.