Functions3
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 teststest {
let source = @moonseed.Replay::new(b"\x17\x90")
assert_eq(@moonseed.below(b"\x03\xe8", source), b"\x03\x90")
}
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 teststest {
let source = @moonseed.Replay::new(b"\x01\x02\x03")
assert_eq(@moonseed.bytes(2, source), b"\x01\x02")
}
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 teststest {
let source = @moonseed.Replay::new(b"\xc0\x80")
assert_eq(@moonseed.scalar(b"\x05", source), b"\x04")
}
Types1
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 teststest {
let source = @moonseed.Replay::new(b"\x0a")
let buf = FixedArray::make(1, b'\x00')
source.fill(buf)
assert_eq(buf, [b'\x0a'])
}
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 teststest {
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)
}
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 teststest {
let source = @moonseed.Replay::new(b"\x01\x02")
assert_eq(source.remaining(), 2)
}
pub fn Replay::remaining(self : Replay) -> Int32
How many bytes are left to give.
runs in the teststest {
let source = @moonseed.Replay::new(b"\x0a\x0b")
let buf = FixedArray::make(1, b'\x00')
source.fill(buf)
assert_eq(source.remaining(), 1)
}
Errors1
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 teststest {
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
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 teststest {
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'])
}
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.