{"record":{"id":"a8b5d1a967203f59","repo":"crowdsecurity/crowdsec","slug":"generate-challenge-nonce-w","errorCode":null,"errorMessage":"generate challenge nonce: %w","messagePattern":"generate challenge nonce: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/appsec/challenge/ticket.go","lineNumber":60,"sourceCode":")\n\n// ticketAgeBackstop is a loose ceiling on accepted submission age in\n// verifyChallenge. The actual freshness gate is the keyring live\n// window (rotationInterval × maxLiveEpochs); this is a separate ceiling\n// that protects against operators configuring an unusually wide live\n// window. Loose enough not to interfere with real submissions on slow\n// clients (high-difficulty PoW) but tight enough to bound replay\n// surface in pathological configurations.\nconst ticketAgeBackstop = 20 * time.Minute\n\n// generateChallengeNonce returns a fresh 16-byte random per-challenge nonce\n// (`r`) as hex. `r` keys single-use bookkeeping (spent_set.go) and seeds the\n// per-challenge secret `s = HMAC(K_epoch, r)`. Error (not panic) on entropy\n// failure so only the current request fails.\nfunc generateChallengeNonce() (string, error) {\n\tbuf := make([]byte, 16)\n\tif _, err := crand.Read(buf); err != nil {\n\t\treturn \"\", fmt.Errorf(\"generate challenge nonce: %w\", err)\n\t}\n\n\treturn hex.EncodeToString(buf), nil\n}\n\n// deriveChallengeSecret computes the per-challenge signing secret\n// `s = HMAC(K_epoch, r)` (hex). `s` is never transmitted; client and server\n// derive it independently from the same per-epoch key.\nfunc deriveChallengeSecret(signKey []byte, r string) string {\n\treturn hmacSHA256Hex(signKey, []byte(r))\n}\n\n// epochForTimestamp converts a nanosecond UnixNano string (the format used in\n// challenge.go's ts) into the keyring's epoch identifier. Uses the same\n// rotation interval as the keyring so two instances always agree.\nfunc (c *ChallengeRuntime) epochForTimestamp(ts string) int64 {\n\ttsVal, err := strconv.ParseInt(ts, 10, 64)\n\tif err != nil || tsVal <= 0 {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/ticket.go#L42-L78","documentation":"generateChallengeNonce reads 16 bytes of crypto/rand entropy and hex-encodes them as the per-challenge nonce `r` used to derive the challenge signing secret. The error wraps a crypto/rand read failure, meaning the OS entropy source could not supply bytes. It is thrown as an error (not a panic) so only the current request fails instead of the whole WAF.","triggerScenarios":"Calling generateChallengeNonce (directly in tests, or via GetChallengePage/freshChallenge) when crand.Read fails — typically when the OS random source (/dev/urandom, getrandom syscall) is unavailable or returns an error.","commonSituations":"Hardened containers with restricted syscalls blocking getrandom, exotic sandboxes/seccomp profiles, or heavily degraded kernel entropy conditions. Very rare on normal Linux systems.","solutions":["Verify the kernel random source works: run `head -c 16 /dev/urandom | xxd` on the host running crowdsec","Check container seccomp/apparmor profiles allow the getrandom(2) syscall","Restart the host/container to restore the entropy subsystem","Update to a newer Go/runtime version if the failure is a known getrandom bug on your platform"],"exampleFix":"// before\nnonce, err := generateChallengeNonce()\nif err != nil { panic(err) }\n// after\nnonce, err := generateChallengeNonce()\nif err != nil {\n    log.Errorf(\"challenge nonce unavailable: %v\", err)\n    http.Error(w, \"internal error\", http.StatusInternalServerError)\n    return\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":null,"tryCatchPattern":"nonce, err := generateChallengeNonce()\nif err != nil {\n    log.Errorf(\"challenge nonce unavailable: %v\", err)\n    // fail only this request\n    return err\n}","preventionTips":["Keep the default getrandom(2) path available in containers (don't block it with seccomp)","Monitor for recurring crypto/rand failures in logs","Pin tested Go versions for your platform"],"tags":["crypto","entropy","appsec"],"backgroundTag":"entropy-source-unavailable","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}