crowdsecurity/crowdsec · error

challenge response already used

Error message

challenge response already used

What it means

Replay protection failure in ValidateChallengeResponse: the nonce/ticket material in this response has already been consumed (single-use enforcement), so an identical or replayed submission is rejected.

Source

Thrown at pkg/appsec/challenge/challenge.go:683

	powHash := sha256.Sum256([]byte(clientPowSalt + clientNonce))
	if !hasLeadingZeroBits(powHash[:], clientDifficulty) {
		return nil, FingerprintData{}, 0, ErrChallengePoW
	}

	// Verify the submission signature sig = HMAC(s, r||ts||n||f), where the
	// secret s = HMAC(K_epoch, r) is never transmitted — a valid sig proves the
	// client derived s from the per-epoch key in the obfuscated dynamic module.
	s := deriveChallengeSecret(signKey, clientR)

	expectedSig := hmacSHA256Hex([]byte(s), []byte(clientR+clientTS+clientNonce+encryptedFingerprint))
	if !hmac.Equal([]byte(clientSig), []byte(expectedSig)) {
		return nil, FingerprintData{}, 0, errors.New("invalid HMAC in challenge response")
	}

	// Single-use: burn `r` (rejects replays). Done last so the spent-set only
	// grows on fully-valid submissions.
	if !c.spent.checkAndInsert(clientR, ticketAgeBackstop) {
		return nil, FingerprintData{}, 0, errors.New("challenge response already used")
	}

	obfKey := deriveFingerprintObfKey(s, clientR)

	fingerprint, err := deobfuscateFingerprint(obfKey, encryptedFingerprint)
	if err != nil {
		return nil, FingerprintData{}, 0, fmt.Errorf("failed to deobfuscate fingerprint: %w", err)
	}

	var fpData FingerprintData

	if err := json.Unmarshal([]byte(fingerprint), &fpData); err != nil {
		return nil, FingerprintData{}, 0, fmt.Errorf("%w: failed to unmarshal fingerprint data: %w", ErrChallengePayload, err)
	}

	// Debug diagnostic: a validated submission. Guarded so `k_epoch` (forgeable
	// signing material — DESIGN.md §2.1) is only formatted at debug.
	if c.log().Logger.IsLevelEnabled(log.DebugLevel) {

View on GitHub (pinned to 909b515798)

Solutions

  1. Treat as replay: deny the request and consider escalating remediation for repeated replays
  2. Investigate caching layers (CDN, proxy) that might resubmit a previously served challenge response
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at pkg/appsec/challenge/challenge.go:683 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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