moonseed0.2.0  ·  Apache-2.0

moonbitstack / moonseed · reference

API

Every public item of both packages: the signature the source declares, and the note beside it. The list of items is the compiler's interfaces; the prose is the source's own comments, so this page cannot describe an API the library does not have.

13 public items 2 packages 0.2.0 rev

moonbitstack/moonseed · moonbitstack/moonseed/system · the guide

01

moonbitstack/moonseed

3 functions · 1 error · 1 type · 1 trait

Functions3

function

below

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

A uniform number in [0, bound), big-endian and as wide as bound.

Draws as many bytes as bound - 1 needs, clears the unused high bits of the first, and draws again while the value is at or above bound. Rejection rather than modulo: reducing a wider draw modulo bound would favour the small values. A bound of 1 reads nothing. Aborts when bound is zero.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\x17\x90")
  assert_eq(@moonseed.below(b"\x03\xe8", source), b"\x03\x90")
}
function

bytes

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

n uniform random bytes, no more and no fewer.

Aborts on a negative n: that is a mistake in the caller, and handing back an empty buffer would hide it.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\x01\x02\x03")
  assert_eq(@moonseed.bytes(2, source), b"\x01\x02")
}
function

scalar

#
pub 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.

Draws as FIPS 186-5 A.2.2 says: the leftmost bits of a draw as long as order, drawn again while the value is zero or at or above order. A P-521 scalar is 66 bytes with its top bits clear, which is why this cannot be "ask for 32 bytes". Aborts when order is below 2.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\xc0\x80")
  assert_eq(@moonseed.scalar(b"\x05", source), b"\x04")
}

Types1

type

Replay

#
pub struct Replay

A source that hands out the bytes it was given, in order, and is exhausted after.

Built from bytes alone, so a draw can be pinned to a fixed sequence without a platform in sight: the fixed randomness of a test vector, or a test that walks every branch of a rejection loop.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\x0a")
  let buf = FixedArray::make(1, b'\x00')
  source.fill(buf)
  assert_eq(buf, [b'\x0a'])
}
method

Replay::fill

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

Copy the next bytes of the replay into buf.

It refuses whole: when fewer bytes remain than buf wants, nothing is copied, remaining() is unchanged, and Exhausted is raised. A half-filled buffer is the kind of thing a caller forgets to check.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\x07\x08")
  let buf = FixedArray::make(2, b'\x00')
  source.fill(buf)
  assert_eq(buf, [b'\x07', b'\x08'])
  assert_eq(source.remaining(), 0)
}
method

Replay::new

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

Build a source from the bytes a draw should see, in order.

The bytes are held as given; nothing is copied twice, and nothing is generated when they run out.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\x01\x02")
  assert_eq(source.remaining(), 2)
}
method

Replay::remaining

#
pub fn Replay::remaining(self : Replay) -> Int32

How many bytes are left to give.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\x0a\x0b")
  let buf = FixedArray::make(1, b'\x00')
  source.fill(buf)
  assert_eq(source.remaining(), 1)
}

Errors1

error

Unavailable

#
pub(all) suberror Unavailable {
  Platform
  Exhausted
  Failed(String)
}

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

The three cases are told apart because they call for different answers: a platform without a source cannot be retried, a spent replay is a mistake in the test, and a failed source names its own reason.

Platform
The platform has no entropy source, or its source failed.
Exhausted
A Replay source has given all the bytes it holds.
Failed(String)
A source of the program's own failed, for the reason given.
runs in the tests
test {
  let spent = try {
    ignore(@moonseed.bytes(1, @moonseed.Replay::new(b"")))
    None
  } catch {
    e => Some(e)
  }
  assert_eq(spent, Some(@moonseed.Unavailable::Exhausted))
  assert_eq(
    @moonseed.Unavailable::Failed("no entropy left").to_string(),
    "no entropy left",
  )
}

Traits1

trait

Source

#
pub(open) trait Source

Where the bytes come from.

Open, so a program can plug in a hardware source, a service, or a fixed sequence in a test, and this library needs to know about none of them. The platform's own source is @system.source.

runs in the tests
test {
  let source = @moonseed.Replay::new(b"\x2a\x2b")
  let buf = FixedArray::make(2, b'\x00')
  source.fill(buf)
  assert_eq(buf, [b'\x2a', b'\x2b'])
}
method

Source::fill

#
fn fill(Self, FixedArray[Byte]) -> Unit raise Unavailable

Fill all of buf, or raise. A source that fills part of it and returns has broken the contract: the caller has no way to tell how much is real.

02

moonbitstack/moonseed/system

1 value · 1 type

Types1

type

System

#
pub struct System

The platform's entropy source. It holds nothing; every fill asks the platform again.

Holding nothing is the point: a cached buffer is a copy of the platform's secret that this library would then be responsible for wiping, and a stale one after a fork or a resume.

method

System::fill

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

Fails with Platform when the platform has no source or reports a failure, and never falls back to anything else.

A host without entropy is a host whose secrets cannot be guessed only by accident; inventing bytes for it would turn that into a silent weakness.

Values1

value

source

#
pub let source : System

The one value there is a need for.

runs in the tests
test {
  // Nothing to construct and nothing to close: the value carries no state,
  // and each fill asks the platform again.
  ignore(@system.source)
}