crowdsecurity/crowdsec · critical
generate PoW prefix: %w
Error message
generate PoW prefix: %w
What it means
generatePowPrefix reads powSaltBytes of crypto/rand entropy to build the hex-encoded salt/prefix the client must hash for the proof-of-work challenge. The error wraps a crypto/rand read failure. Like the nonce generator, it returns an error rather than panicking so one failing request does not take down the WAF; the caller can reject the current challenge and let the client retry.
Source
Thrown at pkg/appsec/challenge/ticket.go:105
// block, which is what makes it fast enough to justify the difficulty levels
// above. That budget is 55 bytes, and a base36 nonce can reach 11 chars, so the
// hex salt has to fit in the rest. The solver refuses to run on a salt that
// doesn't, this is the server's half of
// the contract. TestPowSaltFitsClientFastPath guards it.
const (
powSaltBytes = 16
powSaltMaxHexLen = 44
)
// generatePowPrefix returns a freshly-generated random PoW salt rendered as a
// hex string. Errors from crypto/rand.Read indicate a broken kernel entropy
// pool, which is recoverable at the request layer (we can reject the current
// challenge and let the client retry) — returning the error rather than
// panicking keeps a single failing request from taking down the whole WAF.
func generatePowPrefix() (string, error) {
buf := make([]byte, powSaltBytes)
if _, err := crand.Read(buf); err != nil {
return "", fmt.Errorf("generate PoW prefix: %w", err)
}
return hex.EncodeToString(buf), nil
}
// computePowMAC authenticates a PoW salt as server-generated and bound to a
// specific challenge (`r`) + timestamp + difficulty, signed with the per-epoch
// key.
func (c *ChallengeRuntime) computePowMAC(salt, r, ts string, difficulty int) string {
epoch := c.epochForTimestamp(ts)
signKey, ok := c.keys.SignKey(epoch)
if !ok {
_, signKey = c.keys.Current()
}
h := hmac.New(sha256.New, signKey)
h.Write([]byte(salt))
h.Write([]byte(r))View on GitHub (pinned to 909b515798)
Solutions
- Check the OS entropy source: `head -c 16 /dev/urandom | xxd`
- Review container syscall filters (seccomp) to ensure getrandom(2) is allowed
- Restart the machine or container to recover the random subsystem
- If persistent, inspect dmesg for entropy/cr ng related kernel errors
Defensive patterns
Strategy: try-catch
Validate before calling
null
Try / catch
prefix, err := generatePowPrefix()
if err != nil {
log.Errorf("pow prefix unavailable: %v", err)
// reject this challenge, client can retry
return err
} Prevention
- Same entropy hygiene as nonce generation: allow getrandom in sandbox profiles
- Alert on entropy-source errors before they block challenge issuance
When it happens
Trigger: Calling generatePowPrefix (via GetChallengePage or mustGeneratePowPrefix) when crand.Read fails to deliver bytes from the OS entropy source.
Common situations: Same as nonce generation: seccomp profiles blocking getrandom in hardened containers, broken /dev/urandom, unusual platforms. Practically never seen on healthy Linux hosts.
Related errors
- failed to generate nonce: %w
- generate challenge nonce: %w
- unable to generate a new random seed for JWT generation
- not enough entropy at random seed generation for JWT generat
- cookie expired
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/45fd57b5e19450bc.
Report an issue: GitHub.