microsoft / SEAL

Microsoft SEAL is an easy-to-use and powerful homomorphic encryption library.
https://www.microsoft.com/en-us/research/group/cryptography-research/
MIT License
3.58k stars 709 forks source link

Self-contained WASM library #429

Open rickwebiii opened 2 years ago

rickwebiii commented 2 years ago

The TL;DR; of my scenario is I'm making Rust bindings that can target most sane native targets (works today through the C API) and wasm32 (does not work today). Rust has 4 WASM targets, (wasm32-unknown-unknown, wasm-wasi, wasm64-unknown-unknown, and wasm32-emscripten). The first two targets are important, the third will be important, and the fourth is deprecated and not really encouraged in the Rust ecosystem.

My understanding of the situation today, if you want SEAL to target WASM, you more or less have to write your application in C++, statically link against libseal, and run emcc on your binary/library to generate a self-contained .wasm and .js bundle of your entire application. At least, this is how the benchmarks and tests work in WASM.

To improve interop with other languages under WASM, I'd like to compile the existing C bindings and export all of their functions in a single .wasm file with no corresponding js file. In principle, emcc allows this with -s STANDALONE_WASM, -o seal.wasm, -s EXPORTED_FUNCTIONS=@path/to/c/bindings. However, the C bindings assert 64-bit at compile time, which is untrue in wasm32 targets and I think CMake enforces you don't attempt to build the C bindings.

The work I see to making this work:

s0l0ist commented 2 years ago

Are you trying to build a more universal set of bindings in WASM for integration with other languages? Ex: to be able to interface SEAL-wasm with golang-wasm builds?

It would be amazing to have rust bindings for SEAL. I tried going down a similar path a while back but ran into a few roadblocks, namely the decoupling of CMake so rust could have full control of the build args. I didn't have capacity nor the insight on how to do this correctly.

Technically, you don't need to build an application in C/C++, link SEAL, and compile to WASM if you already have bindings in a given language. For example, you could write a TypeScript application using node-seal or an app in C/F# using the .NET Nuget package. If however, you mean a more performant WASM app (specifically for JS or TS), then yes, you'd need to build all logic in c/c++ and build a statically linked WASM binary.

In the past, I've co-authored a C/C++ PSI library leveraging Bazel to compile to different targets and support other polyglot bindings with ease. Rust bindings included - the difference between that project and SEAL was I didn't have CMake in the way. The polyglot support centered around a C/C++ core and writing bindings for other languages including WASM (via emscripten), Rust, Python, and Golang. This might be an easier approach rather than trying to build a WASM interface as the core. Maybe that can be a source of inspiration.

rickwebiii commented 2 years ago

Building Rust bindings for aarch64 (and x86_64, etc) wasn't too bad; simply use the cmake crate to compile SEAL in a build.rs file with C bindings and as a static library. Then do

    println!("cargo:rustc-link-lib=static=sealc-3.7");
    println!("cargo:rustc-link-lib=static=seal-3.7");

and Rust will link these libraries in with your SEAL bindings crate. You then run bindgen somewhat carefully on the C bindings header files, and Clang will spit out the unsafe FFI code you call from Rust, and then spend a week writing good bindings that capture Rust's idioms and hide the unsafe code. It is rocket science in that you need to be pretty knowledgeable in both Rust and C/C++ toolchains, but it's a very common and well understood strategy that many wrapper crates employ.

We can and do run Rust applications that use SEAL today, so long as we target our machine's native architecture.

We want to support Wamer/Wasmtime environments which don't have a Javascript runtime (in addition to browsers), so emscripten's .js bindings aren't sufficient. Additionally, we'd need to special case our rust bindings to go through Javascript when targeting WASM, which isn't very clean.