Sign in

    moonrand

    Secure random bytes and unbiased sampling for MoonBit; never a seeded generator.

    random
    entropy
    csprng
    crypto
    moonbit
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    1 hour ago
    Downloads
    9

    No dependencies

    #moonrand

    Secure random bytes, and numbers drawn from them without bias, over any entropy source the caller supplies.

    test "draw" {
    let key = @moonrand.bytes(32, @system.source) // 32 uniform bytes
    let n = @moonrand.below(b"\x03\xe8", @system.source) // uniform in [0, 1000)
    let d = @moonrand.scalar(b"\x05", @system.source) // uniform in [1, 4]
    ignore((key, n, d))
    }

    moon run src/examples/tour shows all of it in one run.

    #Packages

    PackageWhat it holdsDepends on
    moonrandSource, Unavailable, bytes, below, scalar, Replaynothing
    moonrand/systemsource, the platform's entropycore env

    The root package touches no platform. Linking system links the platform's call, which on wasm is the WASI random_get import; a program for a wasm host without WASI uses the root package with a source of its own.

    #Sources

    SourceWhere the bytes come from
    @system.sourcecrypto.getRandomValues on JavaScript, WASI random_get on wasm and wasm-gc, the runtime on native
    Replay::new(bytes)the bytes given, in order; exhausted after
    your ownanything that implements Source

    struct Device {}

    impl @moonrand.Source for Device with fn fill(_self, _buf) {
    raise Failed("the device is unplugged")
    }

    No source falls back to a seeded generator. When there are no bytes to be had, the call raises Unavailable: Platform from the platform source, Exhausted from a replay, Failed(reason) from a source of your own.

    #Sampling

    below(bound, source) reads bound as a big-endian number and returns one in [0, bound), as wide as bound. It draws as Go's crypto/rand.Int does: as many bytes as bound - 1 needs, the unused high bits of the first cleared, drawn again while the value is at or above bound. Every value is equally likely; reducing a wider draw modulo bound would favour the small ones.

    scalar(order, source) returns a value in [1, order - 1], as wide as order: a private key or a nonce for a group of that order. It draws as FIPS 186-5 A.2.2 and Go's ECDSA do: the leftmost bits of a draw as long as order, drawn again while the value is zero or not below order. A P-521 scalar is 66 bytes with the top bits clear, which is why this is not "ask for 32 bytes".

    Both are checked against Go: scripts/gen_vectors.go has crypto/rand.Int and ecdsa.GenerateKey draw from fixed byte streams, and the tests replay those bytes and expect the same values, every byte used.

    #Licence

    Apache-2.0.

    Source

    pub(open) trait Source {
    fn fill(Self, FixedArray[Byte]) -> Unit raise Unavailable
    }

    Where random bytes come from: Rust's RngCore::try_fill_bytes. The platform's source is @system.source; a program implements this for a hardware source, a service, or a fixed sequence in its tests.

    Unavailable

    pub(all) suberror Unavailable {
    Platform
    Exhausted
    Failed(String)
    } derive(Eq,
    Debug
    )

    No random bytes could be had. There is no fallback to a seeded generator: a caller that needs entropy and gets none stops here.
    impl Show for Unavailable

    Unavailable::equal

    fn Unavailable::equal(Unavailable, Unavailable) -> Bool

    Unavailable::not_equal

    fn Unavailable::not_equal(x : Unavailable, y : Unavailable) -> Bool

    Unavailable::output

    fn Unavailable::output(self : Unavailable, logger : &Logger) -> Unit

    Unavailable::to_string

    fn Unavailable::to_string(self : Unavailable) -> String

    Replay

    pub struct Replay {
    // private fields
    }

    A source that hands out the bytes it was given, in order, and is exhausted after: the fixed randomness of a test vector, or a test that walks every branch of a rejection loop.
    impl Source for Replay

    Replay::fill

    fn Replay::fill(self : Replay, buf : FixedArray[Byte]) -> Unit raise Unavailable

    Replay::new

    fn Replay::new(data : Bytes) -> Replay

    Replay::remaining

    fn Replay::remaining(self : Replay) -> Int

    How many bytes are left to give.

    below

    fn[S : Source] below(bound : BytesView, source : S) -> Bytes raise Unavailable

    A uniform number in [0, bound), both big-endian and as wide as bound, drawn as Go's crypto/rand.Int draws it and from the same bytes: as many bytes as bound - 1 needs, the unused high bits of the first cleared, drawn again while the value is at or above bound. No value is likelier than another; reducing a wider draw modulo bound would favour the small ones. A bound of 1 reads nothing. Aborts when bound is zero.

    bytes

    fn[S : Source] bytes(n : Int, source : S) -> Bytes raise Unavailable

    n uniform random bytes: Go's rand.Read, Python's secrets.token_bytes. Aborts on a negative n.

    scalar

    fn[S : Source] scalar(order : BytesView, source : S) -> Bytes raise Unavailable

    A uniform number in [1, order - 1], as wide as order: the private key or nonce of a group of that order, drawn as FIPS 186-5 A.2.2 and Go's ECDSA draw it and from the same bytes: the leftmost bits of a draw as long as order, drawn again while the value is zero or at or above order. Aborts when order is below 2.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io