Add secure_random device to stdenv

Co-authored-by: Patrick Ferris <pf341@patricoferris.com>
This commit is contained in:
Thomas Leonard 2022-02-02 11:01:58 +00:00
parent 781dc09b24
commit af27a3008a
10 changed files with 152 additions and 0 deletions

View File

@ -36,6 +36,7 @@ module Stdenv = struct
clock : Time.clock;
fs : Dir.t;
cwd : Dir.t;
secure_random : Flow.source;
>
let stdin (t : <stdin : #Flow.source; ..>) = t#stdin
@ -44,6 +45,7 @@ module Stdenv = struct
let net (t : <net : #Net.t; ..>) = t#net
let domain_mgr (t : <domain_mgr : #Domain_manager.t; ..>) = t#domain_mgr
let clock (t : <clock : #Time.clock; ..>) = t#clock
let secure_random (t: <secure_random : #Flow.source; ..>) = t#secure_random
let fs (t : <fs : #Dir.t; ..>) = t#fs
let cwd (t : <cwd : #Dir.t; ..>) = t#cwd
end

View File

@ -451,6 +451,10 @@ module Flow : sig
inherit read
end
val read_exact : #source -> Cstruct.t -> unit
(** [read_exact src dst] keeps reading into [dst] until it is full.
@raise End_of_file if the buffer could not be filled. *)
val string_source : string -> source
val cstruct_source : Cstruct.t list -> source
@ -903,6 +907,7 @@ module Stdenv : sig
clock : Time.clock;
fs : Dir.t;
cwd : Dir.t;
secure_random : Flow.source;
>
val stdin : <stdin : #Flow.source as 'a; ..> -> 'a
@ -913,6 +918,9 @@ module Stdenv : sig
val domain_mgr : <domain_mgr : #Domain_manager.t as 'a; ..> -> 'a
val clock : <clock : #Time.clock as 'a; ..> -> 'a
val secure_random : <secure_random : #Flow.source as 'a; ..> -> 'a
(** [secure_random t] is a source of random bytes suitable for cryptographic purposes. *)
val cwd : <cwd : #Dir.t as 'a; ..> -> 'a
(** [cwd t] is the current working directory of the process (this may change
over time if the process does a `chdir` operation, which is not recommended). *)

View File

@ -21,6 +21,12 @@ let read_into (t : #read) buf =
let read_methods (t : #read) = t#read_methods
let rec read_exact t buf =
if Cstruct.length buf > 0 then (
let got = read_into t buf in
read_exact t (Cstruct.shift buf got)
)
class virtual source = object (_ : #Generic.t)
method probe _ = None
inherit read

View File

@ -651,6 +651,11 @@ let fstat fd =
external eio_mkdirat : Unix.file_descr -> string -> Unix.file_perm -> unit = "caml_eio_mkdirat"
external eio_getrandom : Cstruct.buffer -> int -> int -> int = "caml_eio_getrandom"
let getrandom { Cstruct.buffer; off; len } =
eio_getrandom buffer off len
(* We ignore [sw] because this isn't a uring operation yet. *)
let mkdirat ~perm dir path =
wrap_errors path @@ fun () ->
@ -864,6 +869,7 @@ module Objects = struct
clock : Eio.Time.clock;
fs : Eio.Dir.t;
cwd : Eio.Dir.t;
secure_random : Eio.Flow.source;
>
let domain_mgr ~run_event_loop = object (self)
@ -948,6 +954,12 @@ module Objects = struct
mkdirat ~perm None path
end
let secure_random = object
inherit Eio.Flow.source
method read_methods = []
method read_into buf = getrandom buf
end
let stdenv ~run_event_loop =
let of_unix fd = FD.of_unix_no_hook ~seekable:(FD.is_seekable fd) ~close_unix:true fd in
let stdin = lazy (source (of_unix Unix.stdin)) in
@ -963,6 +975,7 @@ module Objects = struct
method clock = clock
method fs = (fs :> Eio.Dir.t)
method cwd = (cwd :> Eio.Dir.t)
method secure_random = secure_random
end
end

View File

@ -136,6 +136,14 @@ val accept : sw:Switch.t -> FD.t -> (FD.t * Unix.sockaddr)
val shutdown : FD.t -> Unix.shutdown_command -> unit
(** Like {!Unix.shutdown}. *)
(** {1 Randomness} *)
val getrandom : Cstruct.t -> int
(**[ getrandom buf] reads some random bytes into [buf] and returns the number of bytes written.
It uses Linux's [getrandom] call, which is like reading from /dev/urandom
except that it will block (the whole domain) if used at early boot
when the random system hasn't been initialised yet. *)
(** {1 Eio API} *)
module Objects : sig
@ -152,6 +160,7 @@ module Objects : sig
clock : Eio.Time.clock;
fs : Eio.Dir.t;
cwd : Eio.Dir.t;
secure_random : Eio.Flow.source;
>
val get_fd : <has_fd; ..> -> FD.t

View File

@ -1,11 +1,13 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/eventfd.h>
#include <sys/random.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/signals.h>
#include <caml/unixsupport.h>
#include <caml/bigarray.h>
CAMLprim value caml_eio_eventfd(value v_initval) {
int ret;
@ -27,3 +29,14 @@ CAMLprim value caml_eio_mkdirat(value v_fd, value v_path, value v_perm) {
if (ret == -1) uerror("mkdirat", v_path);
CAMLreturn(Val_unit);
}
CAMLprim value caml_eio_getrandom(value v_ba, value v_off, value v_len) {
CAMLparam1(v_ba);
ssize_t ret;
void *buf = Caml_ba_data_val(v_ba) + Long_val(v_off);
caml_enter_blocking_section();
ret = getrandom(buf, Long_val(v_len), 0);
caml_leave_blocking_section();
if (ret == -1) uerror("getrandom", Nothing);
CAMLreturn(Val_long(ret));
}

View File

@ -228,6 +228,12 @@ module File = struct
await_with_cancel ~request (fun loop -> Luv.File.mkdir ~loop ~request ~mode path)
end
module Random = struct
let fill buf =
let request = Luv.Random.Request.make () in
await_with_cancel ~request (fun loop -> Luv.Random.random ~loop ~request buf) |> or_raise
end
module Stream = struct
type 'a t = [`Stream of 'a] Handle.t
@ -486,6 +492,18 @@ module Objects = struct
socket sock
end
let secure_random =
object
inherit Eio.Flow.source
method read_methods = []
method read_into buf =
let ba = Cstruct.to_bigarray buf in
Random.fill ba;
Cstruct.length buf
end
type stdenv = <
stdin : source;
stdout : sink;
@ -495,6 +513,7 @@ module Objects = struct
clock : Eio.Time.clock;
fs : Eio.Dir.t;
cwd : Eio.Dir.t;
secure_random : Eio.Flow.source;
>
let domain_mgr ~run_event_loop = object (self)
@ -631,6 +650,7 @@ module Objects = struct
method clock = clock
method fs = (fs :> Eio.Dir.t)
method cwd = (cwd :> Eio.Dir.t)
method secure_random = secure_random
end
end

View File

@ -76,6 +76,11 @@ module File : sig
end
module Random : sig
val fill : Luv.Buffer.t -> unit
(** Wraps {!Luv.Random.random} *)
end
module Handle : sig
type 'a t
@ -113,6 +118,7 @@ module Objects : sig
clock : Eio.Time.clock;
fs : Eio.Dir.t;
cwd : Eio.Dir.t;
secure_random : Eio.Flow.source;
>
val get_fd : <has_fd; ..> -> File.t

53
tests/test_flow.md Normal file
View File

@ -0,0 +1,53 @@
# Setting up the environment
```ocaml
# #require "eio_main";;
```
```ocaml
open Eio.Std
let run fn =
Eio_main.run @@ fun _ ->
fn ()
let mock_source items =
object
inherit Eio.Flow.source
val mutable items = items
method read_methods = []
method read_into buf =
match items with
| [] -> raise End_of_file
| x :: xs ->
let len = min (Cstruct.length buf) (Cstruct.length x) in
Cstruct.blit x 0 buf 0 len;
items <- Cstruct.shiftv (x :: xs) len;
len
end
```
# read_exact
```ocaml
# run @@ fun () ->
let data = List.map Cstruct.of_string ["foo"; "bar"] in
let test n =
let buf = Cstruct.create n in
Eio.Flow.read_exact (mock_source data) buf;
traceln "Got %S" (Cstruct.to_string buf)
in
test 0;
test 3;
test 5;
test 6;
test 7;;
+Got ""
+Got "foo"
+Got "fooba"
+Got "foobar"
Exception: End_of_file.
```

22
tests/test_random.md Normal file
View File

@ -0,0 +1,22 @@
# Setting up the environment
```ocaml
# #require "eio_main";;
```
```ocaml
open Eio.Std
```
# Basic check for randomness
```ocaml
# Eio_main.run @@ fun env ->
let src = Eio.Stdenv.secure_random env in
let b1 = Cstruct.create 8 in
let b2 = Cstruct.create 8 in
Eio.Flow.read_exact src b1;
Eio.Flow.read_exact src b2;
assert (not (Cstruct.equal b1 b2));;
- : unit = ()
```