1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-24 09:17:17 +00:00

Add os/cryptorand.

This commit is contained in:
Andrew Chambers 2019-12-04 11:29:11 +13:00
parent fc53445d08
commit 0ac5b243c7
4 changed files with 95 additions and 5 deletions

View File

@ -25,8 +25,6 @@
#include "util.h" #include "util.h"
#endif #endif
#include <stdlib.h>
#ifndef JANET_REDUCED_OS #ifndef JANET_REDUCED_OS
#include <time.h> #include <time.h>
@ -36,6 +34,8 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
#include <windows.h> #include <windows.h>
#include <direct.h> #include <direct.h>
@ -473,12 +473,13 @@ static Janet os_sleep(int32_t argc, Janet *argv) {
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
Sleep((DWORD)(delay * 1000)); Sleep((DWORD)(delay * 1000));
#else #else
int rc;
struct timespec ts; struct timespec ts;
ts.tv_sec = (time_t) delay; ts.tv_sec = (time_t) delay;
ts.tv_nsec = (delay <= UINT32_MAX) ts.tv_nsec = (delay <= UINT32_MAX)
? (long)((delay - ((uint32_t)delay)) * 1000000000) ? (long)((delay - ((uint32_t)delay)) * 1000000000)
: 0; : 0;
nanosleep(&ts, NULL); RETRY_EINTR(rc, nanosleep(&ts, &ts));
#endif #endif
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -497,6 +498,64 @@ static Janet os_cwd(int32_t argc, Janet *argv) {
return janet_cstringv(ptr); return janet_cstringv(ptr);
} }
static Janet os_cryptorand(int32_t argc, Janet *argv) {
JanetBuffer *buffer;
const char *genericerr = "unable to get sufficient random data";
janet_arity(argc, 1, 2);
int32_t offset;
int32_t n = janet_getinteger(argv, 0);
if (n < 0) janet_panic("expected positive integer");
if (argc == 2) {
buffer = janet_getbuffer(argv, 1);
offset = buffer->count;
} else {
offset = 0;
buffer = janet_buffer(n);
}
/* We could optimize here by adding setcount_uninit */
janet_buffer_setcount(buffer, offset + n);
#ifdef JANET_WINDOWS
for (int32_t i = offset; i < buffer->count; i += sizeof(unsigned int)) {
unsigned int v;
if (rand_s(&v))
janet_panic(genericerr);
for (int32_t j = 0; (j < sizeof(unsigned int)) && (i + j < buffer->count); j++) {
buffer->data[i + j] = v & 0xff;
v = v >> 8;
}
}
#elif defined(__linux__) || defined(__APPLE__)
/* We should be able to call getrandom on linux, but it doesn't seem
to be uniformly supported on linux distros. Macos may support
arc4random_buf, but it needs investigation.
In both cases, use this fallback path for now... */
int rc;
int randfd;
RETRY_EINTR(randfd, open("/dev/urandom", O_RDONLY));
if (randfd < 0)
janet_panic(genericerr);
while (n > 0) {
ssize_t nread;
RETRY_EINTR(nread, read(randfd, buffer->data + offset, n));
if (nread <= 0) {
RETRY_EINTR(rc, close(randfd));
janet_panic(genericerr);
}
offset += nread;
n -= nread;
}
RETRY_EINTR(rc, close(randfd));
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
(void) errmsg;
arc4random_buf(buffer->data + offset, n);
#else
janet_panic("cryptorand currently unsupported on this platform");
#endif
return janet_wrap_buffer(buffer);
}
static Janet os_date(int32_t argc, Janet *argv) { static Janet os_date(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 2); janet_arity(argc, 0, 2);
(void) argv; (void) argv;
@ -981,6 +1040,11 @@ static const JanetReg os_cfuns[] = {
JDOC("(os/cwd)\n\n" JDOC("(os/cwd)\n\n"
"Returns the current working directory.") "Returns the current working directory.")
}, },
{
"os/cryptorand", os_cryptorand,
JDOC("(os/cryptorand n &opt buf)\n\n"
"Get or append n bytes of good quality random data provided by the os. Returns a new buffer or buf.")
},
{ {
"os/date", os_date, "os/date", os_date,
JDOC("(os/date &opt time local)\n\n" JDOC("(os/date &opt time local)\n\n"

View File

@ -228,9 +228,13 @@ typedef struct {
/***** START SECTION TYPES *****/ /***** START SECTION TYPES *****/
#ifdef JANET_WINDOWS
// Must be defined before including stdlib.h
#define _CRT_RAND_S
#endif
#include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <setjmp.h> #include <setjmp.h>
#include <stddef.h> #include <stddef.h>

View File

@ -238,4 +238,26 @@
# Issue #183 - just parse it :) # Issue #183 - just parse it :)
1e-4000000000000000000000 1e-4000000000000000000000
# Ensure randomness puts n of pred into our buffer eventually
(defn cryptorand-check
[n pred]
(def max-attempts 10000)
(var attempts 0)
(while (not= attempts max-attempts)
(def cryptobuf (os/cryptorand 10))
(when (= n (count pred cryptobuf))
(break))
(++ attempts))
(not= attempts max-attempts))
(def v (math/rng-int (math/rng (os/time)) 100))
(assert (cryptorand-check 0 |(= $ v)) "cryptorand skips value sometimes")
(assert (cryptorand-check 1 |(= $ v)) "cryptorand has value sometimes")
(do
(def buf (buffer/new-filled 1))
(os/cryptorand 1 buf)
(assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer")
(assert (= (length buf) 2) "cryptorand appends to buffer"))
(end-suite) (end-suite)

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# Format all code with astyle # Format all code with astyle