mirror of
https://github.com/ocaml-multicore/eio.git
synced 2025-10-04 00:01:56 -04:00
Add secure_random device to stdenv
Co-authored-by: Patrick Ferris <pf341@patricoferris.com>
This commit is contained in:
parent
781dc09b24
commit
af27a3008a
@ -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
|
||||
|
@ -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). *)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
53
tests/test_flow.md
Normal 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
22
tests/test_random.md
Normal 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 = ()
|
||||
```
|
Loading…
x
Reference in New Issue
Block a user