commit ba0fcc9718113d6d2099991945f2db6c562c8309
parent a51a24853d5c04f976f36a4494d05029dd900866
Author: Drew DeVault <sir@cmpwn.com>
Date: Thu, 11 Feb 2021 18:44:01 -0500
crypto::random: new module
Diffstat:
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,
+ };
+};