///|
/// Cryptographically strong random bytes from the platform entropy source.
///
/// This deliberately does NOT go through `@random.Rand::new()`: the standard
/// library silently falls back to a fixed seed when the platform cannot
/// provide entropy, which is catastrophic for key/session material. Here a
/// failed entropy read is a hard error.
pub fn random_bytes(n : Int32) -> Bytes raise {
  guard n >= 0 else { @error.fail("random_bytes: negative length") }
  let out : Array[Byte] = Array(capacity=n)
  while out.length() < n {
    let want = @error.require(
      @env.rand(if n - out.length() < 256 { n - out.length() } else { 256 }),
      "platform entropy source unavailable; refusing to generate key material with a deterministic fallback",
    )
    for b in want {
      out.push(b)
    }
  }
  Bytes::from_array(out)
}

///|
/// A uniform random integer in `[0, max)` — used for Miller-Rabin bases and
/// PKCS#1 padding selection.
pub fn random_below(max : Int32) -> Int32 raise {
  guard max > 0 else { @error.fail("random_below: max must be positive") }
  // Rejection sampling over 31-bit words to avoid modulo bias.
  let limit = 0x7fffffff / max * max
  for ;; {
    let bs = random_bytes(4)
    let w = (bs[0].to_int() << 24) |
      (bs[1].to_int() << 16) |
      (bs[2].to_int() << 8) |
      bs[3].to_int()
    let v = w & 0x7fffffff
    if v < limit {
      return v % max
    }
  }
}