-
Notifications
You must be signed in to change notification settings - Fork 55
Windows fixes #162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ulrikstrid
wants to merge
3
commits into
mirleft:master
Choose a base branch
from
ulrikstrid:windows-entropy
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Windows fixes #162
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| open Lwt | ||
| open Nocrypto | ||
|
|
||
| let chunk = 32 | ||
| and period = 30 | ||
| and device = Nocrypto_entropy_unix.sys_rng | ||
|
|
||
|
|
||
| let mvar_map v f = | ||
| Lwt_mvar.take v >>= fun x -> | ||
| catch (fun () -> f x >>= Lwt_mvar.put v) | ||
| (fun exn -> Lwt_mvar.put v x >>= fun () -> fail exn) | ||
|
|
||
| let some x = Some x | ||
|
|
||
| type t = { | ||
| remove : (unit -> unit) Lwt_sequence.node ; | ||
| g : Rng.g | ||
| } | ||
|
|
||
| let background ~period f = | ||
| let last = ref Unix.(gettimeofday ()) | ||
| and live = ref false | ||
| and period = float period in | ||
| fun () -> | ||
| let t1 = !last | ||
| and t2 = Unix.gettimeofday () in | ||
| if (not !live) && (t2 -. t1 >= period) then begin | ||
| last := t2 ; | ||
| live := true ; | ||
| async @@ fun () -> f () >|= fun () -> live := false | ||
| end | ||
|
|
||
| let attach ~period ?(device = device) g = | ||
| let seed () = | ||
| Lwt.return (Nocrypto_entropy_unix.read_cs chunk) >|= fun buf -> Rng.reseed ~g buf in | ||
| let remove = | ||
| Lwt_sequence.add_r (background ~period seed) Lwt_main.enter_iter_hooks in | ||
| Lwt.return { g ; remove } | ||
|
|
||
| let stop t = | ||
| Lwt_sequence.remove t.remove ; | ||
| catch (fun () -> return_unit) | ||
| Unix.(function Unix_error (EBADF, _, _) -> return_unit | exn -> fail exn) | ||
|
|
||
| let active = Lwt_mvar.create None | ||
|
|
||
| let initialize () = | ||
| Nocrypto_entropy_unix.initialize () ; | ||
| let g = !Rng.generator in | ||
| mvar_map active @@ function | ||
| | Some t when t.g == g -> return (Some t) | ||
| | Some t -> stop t >>= fun () -> attach ~period g >|= some | ||
| | None -> attach ~period g >|= some |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| (** {b RNG} seeding on {b Lwt/Unix}. | ||
|
|
||
| This module provides RNG seeding from the Unix kernel RNG, typically | ||
| {[/dev/urandom]}. It uses {!Lwt} for periodic background reseeding. | ||
|
|
||
| Calling {{!initialize}initialize} is enough to bring the RNG into a working | ||
| state. In addition, a background task is set up to periodically reseed the | ||
| RNG. | ||
|
|
||
| [initialize] is idempotent as long as the default generator is unchanged. | ||
| It is harmless to call it several times. | ||
|
|
||
| Note that [initialize] returns a thread. While the reseeding task has only | ||
| been created once this thread completes, the initial seeding is done before | ||
| the function returns. Is is safe to use the RNG immediately after the | ||
| invocation. | ||
|
|
||
| {1 Usage} | ||
|
|
||
| Seed during module initialization, not waiting for the background seeding to | ||
| start: | ||
| {[let _ = Nocrypto_entropy_lwt.initialize () ]} | ||
|
|
||
| Seed just before the main function, not waiting for the background seeding | ||
| to start: | ||
| {[let () = | ||
| ignore (Nocrypto_entropy_lwt.initialize ()); | ||
| Lwt_main.run (main ()) ]} | ||
|
|
||
| Seed just before the main function, and wait for the background seeding to | ||
| start before proceeding: | ||
| {[let () = | ||
| Lwt_main.run (Nocrypto_entropy_lwt.initialize () >>= main) ]} | ||
|
|
||
| *) | ||
|
|
||
|
|
||
| (** {1 Default generator initialization} *) | ||
|
|
||
| val initialize : unit -> unit Lwt.t | ||
| (** Immediately seeds the current defalt generator using | ||
| {!Nocrypto_entropy_unix.initialize}. The initial seeding is finished before | ||
| the function returns. | ||
|
|
||
| It then invokes {{!attach}attach}. Once the returned thread completes, a | ||
| background reseeding task has been attached to the defalt generator. *) | ||
|
|
||
| (** {1 Background seeding} *) | ||
|
|
||
| type t | ||
| (** Represents background reseeding task. *) | ||
|
|
||
| val attach : period:int -> ?device:string -> Nocrypto.Rng.g -> t Lwt.t | ||
| (** [attach ~period ~device g] instruments the lwt event loop to mix in bytes | ||
| from [device] into [g] whenever external events cause the loop to wake up, | ||
| but no more often than once every [period] seconds. | ||
|
|
||
| [device] defaults to {!Nocrypto_entropy_unix.sys_rng}. *) | ||
|
|
||
| val stop : t -> unit Lwt.t | ||
| (** Stops the reseeding task associated with [t]. Idempotent. *) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Nocrypto_entropy_lwt |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,124 @@ | ||
| #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) | ||
| // | ||
| // endian.h | ||
| // | ||
| // https://gist.github.com/panzi/6856583 | ||
| // | ||
| // I, Mathias Panzenböck, place this file hereby into the public domain. Use | ||
| // it at your own risk for whatever you like. In case there are | ||
| // jurisdictions that don't support putting things in the public domain you | ||
| // can also consider it to be "dual licensed" under the BSD, MIT and Apache | ||
| // licenses, if you want to. This code is trivial anyway. Consider it an | ||
| // example on how to get the endian conversion functions on different | ||
| // platforms. | ||
|
|
||
| #include <sys/endian.h> | ||
| #ifndef PORTABLE_ENDIAN_H__ | ||
| #define PORTABLE_ENDIAN_H__ | ||
|
|
||
| #if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) | ||
|
|
||
| # define __WINDOWS__ | ||
|
|
||
| #endif | ||
|
|
||
| #if defined(__linux__) || defined(__CYGWIN__) | ||
|
|
||
| # include <endian.h> | ||
|
|
||
| #elif defined(__APPLE__) | ||
|
|
||
| /* OS X endian.h doesn't provide be|le macros */ | ||
| #include <machine/endian.h> | ||
| #include <libkern/OSByteOrder.h> | ||
|
|
||
| #define htobe16(x) OSSwapHostToBigInt16(x) | ||
| #define htole16(x) OSSwapHostToLittleInt16(x) | ||
| #define be16toh(x) OSSwapBigToHostInt16(x) | ||
| #define le16toh(x) OSSwapLittleToHostInt16(x) | ||
| #define htobe32(x) OSSwapHostToBigInt32(x) | ||
| #define htole32(x) OSSwapHostToLittleInt32(x) | ||
| #define be32toh(x) OSSwapBigToHostInt32(x) | ||
| #define le32toh(x) OSSwapLittleToHostInt32(x) | ||
| #define htobe64(x) OSSwapHostToBigInt64(x) | ||
| #define htole64(x) OSSwapHostToLittleInt64(x) | ||
| #define be64toh(x) OSSwapBigToHostInt64(x) | ||
| #define le64toh(x) OSSwapLittleToHostInt64(x) | ||
| # include <libkern/OSByteOrder.h> | ||
|
|
||
| # define htobe16(x) OSSwapHostToBigInt16(x) | ||
| # define htole16(x) OSSwapHostToLittleInt16(x) | ||
| # define be16toh(x) OSSwapBigToHostInt16(x) | ||
| # define le16toh(x) OSSwapLittleToHostInt16(x) | ||
|
|
||
| # define htobe32(x) OSSwapHostToBigInt32(x) | ||
| # define htole32(x) OSSwapHostToLittleInt32(x) | ||
| # define be32toh(x) OSSwapBigToHostInt32(x) | ||
| # define le32toh(x) OSSwapLittleToHostInt32(x) | ||
|
|
||
| # define htobe64(x) OSSwapHostToBigInt64(x) | ||
| # define htole64(x) OSSwapHostToLittleInt64(x) | ||
| # define be64toh(x) OSSwapBigToHostInt64(x) | ||
| # define le64toh(x) OSSwapLittleToHostInt64(x) | ||
|
|
||
| # define __BYTE_ORDER BYTE_ORDER | ||
| # define __BIG_ENDIAN BIG_ENDIAN | ||
| # define __LITTLE_ENDIAN LITTLE_ENDIAN | ||
| # define __PDP_ENDIAN PDP_ENDIAN | ||
|
|
||
| #elif defined(__OpenBSD__) | ||
|
|
||
| # include <sys/endian.h> | ||
|
|
||
| #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) | ||
|
|
||
| # include <sys/endian.h> | ||
|
|
||
| # define be16toh(x) betoh16(x) | ||
| # define le16toh(x) letoh16(x) | ||
|
|
||
| # define be32toh(x) betoh32(x) | ||
| # define le32toh(x) letoh32(x) | ||
|
|
||
| # define be64toh(x) betoh64(x) | ||
| # define le64toh(x) letoh64(x) | ||
|
|
||
| #elif defined(__WINDOWS__) | ||
|
|
||
| # include <winsock2.h> | ||
| # include <sys/param.h> | ||
|
|
||
| # if BYTE_ORDER == LITTLE_ENDIAN | ||
| # define htobe16(x) htons(x) | ||
| # define htole16(x) (x) | ||
| # define be16toh(x) ntohs(x) | ||
| # define le16toh(x) (x) | ||
|
|
||
| # define htobe32(x) htonl(x) | ||
| # define htole32(x) (x) | ||
| # define be32toh(x) ntohl(x) | ||
| # define le32toh(x) (x) | ||
|
|
||
| # define htobe64(x) (x) | ||
| # define htole64(x) (x) | ||
| # define be64toh(x) (x) | ||
| # define le64toh(x) (x) | ||
|
|
||
| # elif BYTE_ORDER == BIG_ENDIAN | ||
|
|
||
| /* that would be xbox 360 */ | ||
| # define htobe16(x) (x) | ||
| # define htole16(x) __builtin_bswap16(x) | ||
| # define be16toh(x) (x) | ||
| # define le16toh(x) __builtin_bswap16(x) | ||
|
|
||
| # define htobe32(x) (x) | ||
| # define htole32(x) __builtin_bswap32(x) | ||
| # define be32toh(x) (x) | ||
| # define le32toh(x) __builtin_bswap32(x) | ||
|
|
||
| # define htobe64(x) (x) | ||
| # define htole64(x) __builtin_bswap64(x) | ||
| # define be64toh(x) (x) | ||
| # define le64toh(x) __builtin_bswap64(x) | ||
|
|
||
| # else | ||
|
|
||
| # error byte order not supported | ||
|
|
||
| # endif | ||
|
|
||
| # define __BYTE_ORDER BYTE_ORDER | ||
| # define __BIG_ENDIAN BIG_ENDIAN | ||
| # define __LITTLE_ENDIAN LITTLE_ENDIAN | ||
| # define __PDP_ENDIAN PDP_ENDIAN | ||
|
|
||
| #else | ||
|
|
||
| /* Needs _DEFAULT_SOURCE with glibc */ | ||
| #include <endian.h> | ||
| # error platform not supported | ||
|
|
||
| #endif | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| native/nocrypto_entropy_unix.o |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #include <caml/mlvalues.h> | ||
| #include <windows.h> | ||
| #include <bcrypt.h> | ||
|
|
||
| CAMLprim value | ||
| get_random_bytes(PUCHAR *pbBuffer) | ||
| { | ||
| BCRYPT_ALG_HANDLE phAlgorithm; | ||
| BCryptOpenAlgorithmProvider(&phAlgorithm, BCRYPT_RSA_ALGORITHM, NULL, 0); | ||
| BCryptGenRandom(phAlgorithm, &pbBuffer, 32, 0); | ||
| BCryptCloseAlgorithmProvider(phAlgorithm, 0); | ||
|
|
||
| return Val_int(32); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| open Nocrypto | ||
| open Uncommon | ||
|
|
||
| let a_little = 32 | ||
| let a_lot = 1024 | ||
|
|
||
|
|
||
| let sys_rng = "BCryptGenRandom" | ||
|
|
||
| external get_random_bytes: bytes -> int = "get_random_bytes" | ||
|
|
||
| let read_cs n = | ||
| let buf = Bytes.create n in | ||
| let k = get_random_bytes buf in | ||
| let cs = Cstruct.create k in | ||
| Cstruct.blit_from_bytes buf 0 cs 0 k; | ||
| cs | ||
|
|
||
| let reseed ?(bytes = a_little) ?(device = sys_rng) g = | ||
| let rec feed n = | ||
| if n > 0 then | ||
| let cs = read_cs(n) in | ||
| Rng.reseed ~g cs; | ||
| feed (n - Cstruct.len cs) in | ||
| feed bytes | ||
|
|
||
| let initialize () = | ||
| let g = !Rng.generator in | ||
| if not (Rng.seeded (Some g)) then reseed ~bytes:a_lot g |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| (** {b RNG} seeding on {b Unix}. | ||
|
|
||
| This module provides one-shot RNG seeding from the Unix kernel RNG, | ||
| typically {[/dev/urandom]}. | ||
|
|
||
| Calling {{!initialize}initialize} is enough to bring the RNG into a working | ||
| state. | ||
|
|
||
| [initialize] is idempotent as long as the default generator is unchanged. | ||
| It is harmless to call it several times. | ||
|
|
||
| If you are using {b lwt}, conside using {!Nocrypto_entropy_lwt} as this | ||
| module allows for continuous reseeding. | ||
|
|
||
| Unless you want to recover from an unlikely case of missing system RNG, the | ||
| recommended way to prime the RNG is to invoke [initialize] at the module | ||
| level: | ||
|
|
||
| {[let () = Nocrypto_entropy_unix.initialize () ]} | ||
|
|
||
| *) | ||
|
|
||
| (** {1 Default generator initialization} *) | ||
|
|
||
| val initialize : unit -> unit | ||
| (** Seeds the current defalt generator from the system RNG device if it is | ||
| currently unseeded. | ||
|
|
||
| This is the closest thing to {!Random.self_init} and is a good way to prime | ||
| the RNG. *) | ||
|
|
||
| (** {1 Manual seeding} *) | ||
|
|
||
| val sys_rng : string | ||
| (** Detected system RNG device. *) | ||
|
|
||
| val read_cs : int -> Cstruct.t | ||
|
|
||
| val reseed : ?bytes:int -> ?device:string -> Nocrypto.Rng.g -> unit | ||
| (** [reseed ~bytes ~g] mixes in [bytes] bytes from the system RNG into the | ||
| generator [g]. | ||
|
|
||
| [bytes] default to a small value reasonable for periodic reseeding. | ||
|
|
||
| [device] defaults to {{!sys_rng}sys_rng}. *) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Nocrypto_entropy_unix |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious about the following points:
32as input toget_random_bytes(get_random_bytes : int -> buffer -> unit)?BCryptGenRandom(and also theOpenAlgorithmProvider) returns aNTSTATUS, which should be checked forSTATUS_SUCCESS(as it is now, an error will be silently discarded and the buffer will be used as if it contained random bytes).BCRYPT_RSA_ALGORITHM? I'd have expectedBCRYPT_RNG_ALGORITHMhere (from https://docs.microsoft.com/en-us/windows/desktop/SecCNG/cng-algorithm-identifiers)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest this is more of a proof of concept, but I'm happy to clean it up to try to make a proper implementation.
I would love more pointers on how to do it as I'm still pretty new to this.
I tried the signature you suggest but had some issues with it, but that might be because I called it with the wrong data.
Is it be possible to pass a Cstruct.t directly and set the bytes in there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NB: I'm neither author nor maintainer of ocaml-nocrypto.
not sure what your goal is, and what "proof of concept" means to you. if your goal is only "nocrypto compiles on windows", you don't even have to mess around with C stubs (but can just use 0s). if you want to provide entropy for nocrypto's rng, you should ensure to retrieve random numbers from windows. in case you want others to use your code, you should be very careful to get proper entropy (i.e. check result codes from the windows API, run some tests to check that the call really modifies the buffer passed).
I have not programmed against windows APIs for years - the above comment is from searching for "/dev/urandom on windows" and following the API reference/guidelines from MSDN.
I'm not entirely sure what this means, but you could read how other C functions are used in this library to figure out how it is done -- take a look into the native.ml module. Cstruct.t is just a wrapper (length, offset, bigarray), and to me it looks like the bigarray is passed to C code directly.
I've not looked into anything apart from the
get_random_bytesimplementation of this PR, and don't have a windows installation to test it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proof of concept as in I wanted to be able to build and run a httpaf/H2 based webserver on Windows (which I now can). The initial goal of this PR was to see if there is any interest in this and to prove that it's possible.
As there seems to be a positive response to this I'll happily keep working on it. But since this is a lot lower level than I'm used to I appreciate the feedback.
As I see it what needs to be done in this part is:
Handle the possible errors, either by using a
resultor aoptiondepending on what's possible with FFI.Change to use
BCRYPT_RNG_ALGORITHM.Make the signature
bytes -> int -> int, but with error handling.If there is a possibility to not go via bytes but just use the
Cstruct.tdirectly that would be preferable to the current implementation.