hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit ba0fcc9718113d6d2099991945f2db6c562c8309
parent a51a24853d5c04f976f36a4494d05029dd900866
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu, 11 Feb 2021 18:44:01 -0500

crypto::random: new module

Diffstat:
Acrypto/random/+linux.ha | 42++++++++++++++++++++++++++++++++++++++++++
Acrypto/random/random.ha | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mrt/+linux/syscalls.ha | 8++++++++
3 files changed, 98 insertions(+), 0 deletions(-)

diff --git a/crypto/random/+linux.ha b/crypto/random/+linux.ha @@ -0,0 +1,42 @@ +use rt; +use io; + +// Fills the given buffer with cryptographically random data. If the system is +// unable to provide random data, abort. If you need to handle errors or want to +// use whatever random data the system can provide, even if less than the +// requested amont, use [stream] instead. +export fn buffer(buf: []u8) void = { + let n = 0z; + for (n < len(buf)) { + match (rt::getrandom(buf[n..]: *[*]u8, len(buf), 0)) { + err: rt::errno => switch (err) { + rt::EINTR, rt::EAGAIN => void, + * => abort(), + }, + z: size => { + n += z; + }, + }; + }; +}; + +fn rand_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { + assert(s == stream); + return match (rt::getrandom(buf: *[*]u8, len(buf), 0)) { + err: rt::errno => errno_to_io(err), + n: size => n, + }; +}; + +fn io_errstr(data: *void) str = { + const errno = data: uintptr: int: rt::errno; + return rt::errstr(errno); +}; + +fn errno_to_io(err: rt::errno) io::error = { + let e = io::os_error { + string = &io_errstr, + data = err: uintptr: *void, + }; + return e: io::error; +}; diff --git a/crypto/random/random.ha b/crypto/random/random.ha @@ -0,0 +1,48 @@ +use io; +use rt; + +// An [io::stream] which returns cryptographically random data on reads. Be +// aware, it may return less than you asked for! +export let stream: *io::stream = null: *io::stream; + +@init fn init() void = { + static let s = io::stream { + name = "<random>", + ... + }; + // TODO: Globals referencing other globals + s.reader = &rand_reader; + stream = &s; +}; + +@test fn buffer() void = { + let buf: [4096]u8 = [0...]; + buffer(buf[..]); + + let sum = 0z; + for (let i = 0z; i < len(buf); i += 1) { + sum += buf[i]; + }; + let avg = sum / len(buf); + assert(avg < 0xA0 && avg > 0x60); +}; + +@test fn reader() void = { + let buf: [4096]u8 = [0...]; + let test: []u8 = []; + match (io::read(stream, buf[..])) { + (io::error | io::EOF) => abort(), + n: size => { + test = buf[..n]; + }, + }; + + assert(len(test) > 0); + + let sum = 0z; + for (let i = 0z; i < len(test); i += 1) { + sum += test[i]; + }; + let avg = sum / len(test); + assert(avg < 0xA0 && avg > 0x60); +}; diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -169,3 +169,11 @@ export fn fcntl(fd: int, cmd: int, arg: fcntl_arg) (int | errno) = { n: u64 => n: int, }; }; + +export fn getrandom(buf: *void, bufln: size, flags: uint) (size | errno) = { + return match (wrap_return(syscall3(SYS_getrandom, + buf: uintptr: u64, bufln: u64, flags: u64))) { + err: errno => err, + n: u64 => n: size, + }; +};