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

  1. Check the OS entropy source: `head -c 16 /dev/urandom | xxd`
  2. Review container syscall filters (seccomp) to ensure getrandom(2) is allowed
  3. Restart the machine or container to recover the random subsystem
  4. 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

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


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