Skip to content
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

Providing js_of_ocaml stubs relying on the wasm target. #496

Open
hhugo opened this issue Nov 18, 2021 · 7 comments
Open

Providing js_of_ocaml stubs relying on the wasm target. #496

hhugo opened this issue Nov 18, 2021 · 7 comments

Comments

@hhugo
Copy link
Contributor

hhugo commented Nov 18, 2021

cc @protz

I've been experimenting with providing js_of_ocaml stubs for hacl-star.
Theses stubs can (mostly) be automatically generated from the ctypes bindings and api.json.

Using the hacl-wasm is not very convenient in this case, because it simplifies the api too much and I find myself trying to reverse this simplification.

As an example,

The raw ocaml binding defines

val hacl_Ed25519_sign : output:bytes -> priv:bytes -> message_len:int -> message:bytes -> unit

api.js will expose it as

 val ed25519_sign : priv:string -> message:string -> string array

for the jsoo stubs, I would have to make sure message is of length message_len and take a substring if not.
It would result in the following pseudo js code

function hacl_Ed25519_sign(output, priv, len, message) {
  if(message.lengh != len) { message = message.sub(len) }
  var r = Ed25519.sign(priv, message);
  blit_bytes(r[0], output);
  return 0;
}

Overall, it would be much simpler to be able to call the wasm funcitons directly. Is it possible ? If not, would you be ok to allow it (as a separate package maybe) ?

@msprotz
Copy link
Contributor

msprotz commented Nov 18, 2021

If I understand correctly what you're suggesting, you ended up with the same conclusion I came to. The code is not public yet, but essentially here's what I did locally for a piece of code that requires some crypto primitives...

Primitives.mli, the list of crypto primitives I need

val sha2_256_hash: bytes -> bytes

primitives-native/Primitives.ml, calling hacl-star(ocaml) --> hacl-star-raw(ocaml) --ctypes--> hacl-star(C)

let sha2_256_hash b =
  Hacl.SHA2_256.hash b

primitives-js/Primitives.ml, declaring the existence of primitives that take Uint8Arrays

external whacl_sha2_256_hash: js_u8array -> js_u8array = "whacl_sha2_256_hash"

let sha2_256_hash b =
  H.bytes_of_uint8array (whacl_sha2_256_hash (H.uint8array_of_bytes b))

primitives.js, which re-routes this to a global object called MyCrypto

//Provides:whacl_sha2_256_hash
function whacl_sha2_256_hash(b) {
  return joo_global_object.MyCrypto.sha2_256_hash(b);
}

main.js, which picks implementations for the crypto using the hacl-wasm node.js package

var HaclWasm = require("./wasm/api.js");

var MyCrypto = {
    sha2_256_hash: (b) => HaclWasm.SHA2.hash_256(b)[0],
}

Does this match what you have in mind? If so, I agree that it's a good way to go. I have a couple local patches to api.js that make it work by default both in a browser and in a node context, if you're interested.

@hhugo
Copy link
Contributor Author

hhugo commented Nov 18, 2021

What I had in mind was to not touch the ml files at all and generate the Js stubs automatically (like it's done for c). All this would be easier if I had access to the wasm api directly instead of what api.js provides

@msprotz
Copy link
Contributor

msprotz commented Nov 19, 2021

Oh so you don't want to use what is in bindings/js but rather you want to provide an identical api to the high-level OCaml bindings (hacl-star OPAM package), except you would implement the low-level calling convention of the KReMLin-generated WASM code. Am I following you all right?

@hhugo
Copy link
Contributor Author

hhugo commented Nov 19, 2021

I'm not sure. I don't want to provide an "identical" API, I want the current API to just work with jsoo. The generated jsoo stubs would indeed implement the calling convention required to call wasm.

@msprotz
Copy link
Contributor

msprotz commented Nov 19, 2021

So you would basically like to add some branching somewhere in the ocaml bindings code that either calls to ctypes or to wasm directly with a minimal amount of glue? This is definitely feasible and I can dig up the documentation on the kremlin calling-convention if it's not obvious from the api.js file.

@hhugo
Copy link
Contributor Author

hhugo commented Nov 19, 2021

No. I want to use ctypes and compile it with jsoo. I want to use the ocaml code as it is now.

@msprotz
Copy link
Contributor

msprotz commented Nov 19, 2021

I see. Then in that case I'd be quite curious to see how you implement it. If you have a proof of concept I'd be quite keen to see it. I tried to do what you describe but I ended up with a lot of ctypes-related cruft being generated by js_of_ocaml and it just felt easier to "cut" higher up in the logic and just ditch ctypes altogether.

The calling convention is simple: arrays are addresses in memory. Call loader.reserve to reserve some memory at the top of the stack to lay out your arrays. Invoke a WASM function and pass it immediate integers for integers, or addresses for arrays. Release the reserved memory once the function has returned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants