liboqs
liboqs copied to clipboard
WASM compatibillity
Ideally, this project should be compilable via emscripten to WASM. Adding compatibility and documentation to make this possible should exist.
We haven't tried this before and don't have experience with WASM. Is this something you've tried? If you have specific CMakeFile changes that have to be made to make it work, feel free to submit that as a pull request.
I'm going to leave this here for now that way I can look into making liboqs compatible with WASM: https://stackoverflow.com/questions/70311540/compiling-c-with-external-library-to-wasm-using-wasmer
If you'd like us to look into this, please point to information how to install all required tooling for this sample build script to work? Are there docker images containing such tooling? A quick install on my local machine yields
$ wasimake cmake -GNinja ..
-- The C compiler identification is Clang 9.0.0
-- The ASM compiler identification is unknown
-- Found assembler: /home/mib/.local/bin/wasicc
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/mib/.local/bin/wasicc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Warning: Did not find file Compiler/-ASM
CMake Error at CMakeLists.txt:82 (message):
Unknown or unsupported processor: x86. Override by setting
OQS_PERMIT_UNSUPPORTED_ARCHITECTURE=ON
-- Configuring incomplete, errors occurred!
See also "/home/mib/git/oqs/liboqs/build/CMakeFiles/CMakeOutput.log".
See also "/home/mib/git/oqs/liboqs/build/CMakeFiles/CMakeError.log".
--> Where does this reference to "clang-9" come from (I've got 14 installed on the machine)? Why is the architecture reported to be "x86"? It's x86_64:
$ clang --version
Ubuntu clang version 14.0.0-1ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Here's the result of running the "simple" wasienv install script: Pretty unhelpful output...
$ curl https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh | sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4788 100 4788 0 0 9516 0 --:--:-- --:--:-- --:--:-- 9500
┏━━━━━━━━━┓
┃ ┃
┃ wasi ( env
┃ ┃
┗━━━━━━━━━┛
> Installing wasienv
WARNING: Skipping wasienv as it is not installed.
Collecting wasienv
Downloading wasienv-0.5.4.tar.gz (24 kB)
Preparing metadata (setup.py) ... done
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from wasienv) (2.25.1)
Building wheels for collected packages: wasienv
Building wheel for wasienv (setup.py) ... done
Created wheel for wasienv: filename=wasienv-0.5.4-py3-none-any.whl size=32525 sha256=2db1b2da3f65e7787f941ec91734b467ce25379e3635af615f7f59c8df25b06f
Stored in directory: /home/mib/.cache/pip/wheels/da/7b/be/e1738169616e1732950f1e78969da47b36d9343f83057d23ad
Successfully built wasienv
Installing collected packages: wasienv
Successfully installed wasienv-0.5.4
Collecting wasienv
Using cached wasienv-0.5.4-py3-none-any.whl
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from wasienv) (2.25.1)
Installing collected packages: wasienv
Successfully installed wasienv-0.5.4
> Adding wasienv to bash profile... ✓
sh: 1: /home/mib/.wasienv/bin/wasienv: not found
> wasienv was installed, but doesn't seem to be working :(
I notice there is 36% of assembly code in this project. The design goal of WASM is not to handle any hardware vendor-specific native assembly code. Are there any configuration switches to bypass the assembly code and replace it with its C equivalent code (at the expense of performance, most likely)?
If the native assembly code is mandatory, WASM build will be impossible.
Are there any configuration switches to bypass the assembly code and replace it with its C equivalent code
No, a single such liboqs build option does not exist "out of the box". Arguably, all code/components do exist for most (i.e, the PQClean) algorithms that would allow adding such switch to the cmake
build structures in the project, though.
You could get very far already using the build option combination OQS_DIST_BUILD=OFF OQS_OPT_TARGET=generic OQS_USE_OPENSSL=OFF as that combination should disable including all optimization code parts (i.e., assembly code).
So, please give the above a try, feel free to do a PR if you need this or provide us with information why we should spend work cycles on this: I personally have doubts anyone would want to use slow-running cryptography... If you know of concrete use case(s), please let us know.
Thanks to the advice from @baentsch, the oqs
WASM build works now. Here is a mini doc.
-
Install the the Emscripten
-
Similar to the
oqs
build process, the WASM build process is prefixed with the Emscripten command as follows:
emcmake cmake -GNinja -DOQS_USE_OPENSSL=OFF -DOQS_PERMIT_UNSUPPORTED_ARCHITECTURE=ON ..
emmake ninja
- Done. It is that simple.
By the way, wasmer
should work too if you use the above -D
switches.
The oqs
source code seems structured very well. It has a "generic" target. Its assembly code does not prevent those pure C code from working. Its runtime fails graciously when the build target is not compiled to fulfill a task, most likely because the target like WASM cannot accept assembly code. For example, instead of crashing, it would print out something like this: KEM algorithm BIKE-L1 not enabled!
Keep up with the great work. Thanks.
Just a friendly reminder. I hope you keep in mind that WASM, representing the web, should be considered a legitimate target. If it is considered "generic", that is fine, as long as it contains only pure (free from assembly code, inline or not) C/C++ code that is not contaminated by the vendor-dependent features such as Linux/Windows system calls.
I hope you keep in mind that WASM, representing the web, should be considered a legitimate target.
Valid point. Three questions to this: a) Would it be OK we add reference to the above as a section to https://github.com/open-quantum-safe/liboqs/wiki/Platform-specific-notes-for-building-liboqs ? b) Would you consider contributing a WASM-ready demo environment/docker image (at https://github.com/open-quantum-safe/oqs-demos)? c) Would you consider contributing to https://github.com/open-quantum-safe/profiling, adding comparative (performance) tests between classic and QS-crypto using WASM (e.g. comparing to WebCrypto)? I personally still have doubts WASM-compiled crypto code can be useful if too slow... But then again, signature operations are not necessarily performance-sensitive (unlike heavy-duty stream ciphers or KEXs).
@DevelopDaily When trying out your steps above, the following error appears:
$ emmake ninja
[1/1104] Building C object src/CMakeFiles/oqs.dir/kem/kem.c.o
FAILED: src/CMakeFiles/oqs.dir/kem/kem.c.o
/emsdk/upstream/emscripten/emcc -Iinclude -I../src -fPIC -fvisibility=hidden -Werror -Wall -Wextra -Wpedantic -Wno-unused-command-line-argument -O3 -fomit-frame-pointer -std=gnu11 -MD -MT src/CMakeFiles/oqs.dir/kem/kem.c.o -MF src/CMakeFiles/oqs.dir/kem/kem.c.o.d -o src/CMakeFiles/oqs.dir/kem/kem.c.o -c ../src/kem/kem.c
../src/kem/kem.c:87:30: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
OQS_API int OQS_KEM_alg_count() {
^
void
... lots of the same repeating...
Any idea what's going on here?
To reproduce: docker run -it ubuntu
and in it
apt update && apt upgrade -y && apt install -y cmake git ninja-build python3
git clone https://github.com/emscripten-core/emsdk.git && cd emsdk && ./emsdk install latest && ./emsdk activate latest && source ./emsdk_env.sh
git clone https://github.com/open-quantum-safe/liboqs.git && cd liboqs && mkdir build && cd build && emcmake cmake -GNinja -DOQS_USE_OPENSSL=OFF -DOQS_PERMIT_UNSUPPORTED_ARCHITECTURE=ON .. && ninja
@baentsch
a) Would it be OK we add reference to ... b) Would you consider contributing a WASM-ready demo ... c) Would you consider contributing to ...
Yes for a) For b) and c), I can try...
@baentsch I only tried the procedure on a full-blown Ubuntu 20.04 Desktop and it worked perfectly.
I will try to do it in a docker container and let you know my testing results.
@baentsch I only tried the procedure on a full-blown Ubuntu 20.04 Desktop and it worked perfectly.
I will try to do it in a docker container and let you know my testing results.
Thanks. I also tried on a desktop (VM) but got the same error messages. If you simply run the commands above the error message is output straight away. What really is surprising is that the pretty strange error message "a function declaration without a prototype is deprecated in all versions of C" is output: This simply cannot be the case as liboqs
is built with many different versions of C compilers -- and none of them (bar this /emsdk/upstream/bin/clang
) complains about this... I'd have the impression it doesn't find the header files, but -Iinclude
is present on the compiler invocation... Will wait to hear from you before adding the instructions to the "Platform build" wiki.
@baentsch
../src/kem/kem.c:87:30: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes] OQS_API int OQS_KEM_alg_count() { ^ void
Actually, that is a legitimate error in the oqs
source code.
In the header file kem.h
:
OQS_API int OQS_KEM_alg_count(void);
In the kem.c
:
OQS_API int OQS_KEM_alg_count()
The void
matters to the cutting-edge C
compilers such as clang
, which thinks the function in the kem.h
does not have an implementation and the one in the kem.c
is "a function declaration without a prototype." It is easy to fix the problem by just adding the void
to the function in the kem.c
. That being said, I guess it would take you some time to fix all the missing void
errors.
The WASM build succeeded on my local machine because it used a slightly older compiler, which does not enforce the function prototype. The docker image always uses the latest compiler, which is supposed to fail the build.
Thanks for the analysis, @DevelopDaily !
After the issue #1204 is resolved, you may build a local docker image to build the WASM. I use Ubuntu. I've made a very simple Dockerfile
FROM emscripten/emsdk:latest
RUN apt -y update
RUN apt -y install astyle cmake gcc ninja-build libssl-dev python3-pytest python3-pytest-xdist unzip xsltproc doxygen graphviz python3-yaml
RUN git clone https://github.com/open-quantum-safe/liboqs.git
Here is the procedure.
- Create a directory that contains only the
Dockerfile
- Go to that directory to build a local docker image named
oqs_wasm
:sudo docker build -t oqs_wasm .
- Run that image:
sudo docker run -it oqs_wasm
- Now, you have a fully configured WASM build environment running in a ubuntu container. The
oqs
resides under/src/liboqs
- So, create the build directory under that:
cd /src/liboqs/
mkdir build && cd build
- Now, you are under
/src/liboqs/build
- Similar to the
oqs
build process, the WASM build process is prefixed with the Emscripten command as follows:
emcmake cmake -GNinja -DOQS_USE_OPENSSL=OFF -DOQS_PERMIT_UNSUPPORTED_ARCHITECTURE=ON ..
emmake ninja
- Done
Thanks for the walk-through, @DevelopDaily . Based on this, it should be possible to build a fully working WASM environment in the above even before #1204 is resolved by simply removing one line: https://github.com/open-quantum-safe/liboqs/blob/9dea0430ed764af2f0728793e783f04e5c0cd32d/.CMake/compiler_opts.cmake#L68
@baentsch You are correct.
After removing that line, I just built the WASM in the container successfully.
You may polish the image however you see fit. That pretty much fulfills your earlier request:
b) Would you consider contributing a WASM-ready demo ...
If necessary, I may add a a little description later for the demo. Basically, all the out-of-the-box oqs
tests under /src/liboqs/build/tests
should work automatically, albeit with reduced capabilities because of the skipped assembly code.
For example, you can create a test file whatever_to_be_hashed.txt
. Then, run
node test_hash.js sha256 < whatever_to_be_hashed.txt
You should see the hash of that file.
Thanks for the confirmation, @DevelopDaily ! As I have 0 knowledge of WASM, (where) are there specific crypto tests (beyond hashing as per your suggestion) that we could run/trigger QSC? Or is it as simple as running "our" speed_kem etc. tests? Do they become "WASM executables" or is there a JS-wrapper required to trigger them?
@baentsch
Precisely, it is as simple as running "your" existing tests. There are JS-wrappers to trigger them. A "WASM executable" is composed of two files. Take the speed_kem
as an example, they are speed_kem.js
and speed_kem.wasm
. You run it like this:
node speed_kem.js
Under the hood, the speed_kem.js
is going to deal with the speed_kem.wasm
. The beauty is that the WASM build process uses "your" existing scripts, without any modification, to take care of everything to produce the "WASM executables".
@baentsch This may help visualize how it is run. By the way, some results look unusual. But, that would be a different story worth investigating later.
root@01a2bff21cba:/src/liboqs/build/tests# node speed_kem.js
Configuration info
==================
Target platform: x86-Linux-5.4.0-109-generic
Compiler: clang (15.0.0 (https://github.com/llvm/llvm-project faef447e72a5c63dfb12bb7b02d44c3c916d31cd))
Compile options: [-Werror;-Wall;-Wextra;-Wno-unused-command-line-argument;-O3;-fomit-frame-pointer;-Wbad-function-cast;-Wcast-qual;-Wnarrowing;-Wconversion]
OQS version: 0.7.2-dev
Git commit: 9dea0430ed764af2f0728793e783f04e5c0cd32d (+ local modifications)
OpenSSL enabled: No
AES: C
SHA-2: C
SHA-3: C
OQS build flags: OQS_OPT_TARGET=generic CMAKE_BUILD_TYPE=Release
CPU exts compile-time:
Speed test
==========
Started at 2022-04-28 05:46:34
Operation | Iterations | Total time (s) | Time (us): mean | pop. stdev | High-prec time (ns): mean | pop. stdev
------------------------------ | ----------:| --------------:| ---------------:| ----------:| -------------------------:| ----------:
Classic-McEliece-348864 | | | | | |
keygen | 4 | 3.049 | 762250.000 | 604095.346 | 762249984 | 604095356
encaps | 880 | 3.001 | 3410.227 | 2851.018 | 3401136 | 2849925
decaps | 4634 | 3.006 | 648.684 | 1153.335 | 643073 | 1144094
Classic-McEliece-348864f | | | | | |
keygen | 7 | 3.747 | 535285.714 | 157769.012 | 535285723 | 157768967
encaps | 774 | 3.000 | 3875.969 | 4339.604 | 3870801 | 4338862
decaps | 6441 | 3.000 | 465.766 | 1038.963 | 464214 | 1038765
Classic-McEliece-460896 | | | | | |
keygen | 2 | 3.812 | 1906000.000 | 290000.000 | 1906000000 | 290000000
encaps | 453 | 3.004 | 6631.347 | 5063.708 | 6629140 | 5065509
decaps | 2384 | 3.000 | 1258.389 | 840.669 | 1257550 | 840945
Classic-McEliece-460896f | | | | | |
keygen | 3 | 5.592 | 1864000.000 | 582381.891 | 1864000000 | 582381838
encaps | 353 | 3.004 | 8509.915 | 8257.920 | 8507082 | 8259292
decaps | 2694 | 3.000 | 1113.586 | 433.919 | 1110987 | 427271
Classic-McEliece-6688128 | | | | | |
keygen | 2 | 8.463 | 4231500.000 | 1353500.000 | 4231499904 | 1353500032
encaps | 220 | 3.001 | 13640.909 | 8642.030 | 13640909 | 8642031
decaps | 2172 | 3.000 | 1381.215 | 751.412 | 1379374 | 745581
Classic-McEliece-6688128f | | | | | |
keygen | 2 | 4.352 | 2176000.000 | 139000.000 | 2176000000 | 138999808
encaps | 333 | 3.003 | 9018.018 | 6791.156 | 9012012 | 6791174
decaps | 2110 | 3.000 | 1421.801 | 2146.815 | 1418958 | 2143394
Classic-McEliece-6960119 | | | | | |
keygen | 2 | 4.813 | 2406500.000 | 249500.000 | 2406499968 | 249500032
encaps | 435 | 3.006 | 6910.345 | 4507.338 | 6901150 | 4506638
decaps | 2665 | 3.000 | 1125.704 | 565.715 | 1125328 | 561475
Classic-McEliece-6960119f | | | | | |
keygen | 2 | 4.413 | 2206500.000 | 15500.000 | 2206500096 | 15500032
encaps | 349 | 3.002 | 8601.719 | 5879.799 | 8595989 | 5884765
decaps | 1974 | 3.000 | 1519.757 | 1639.916 | 1517730 | 1639635
Classic-McEliece-8192128 | | | | | |
keygen | 1 | 8.003 | 8003000.000 | 0.000 | 8003000064 | 0
encaps | 359 | 3.000 | 8356.546 | 5573.105 | 8350976 | 5566952
decaps | 1607 | 3.001 | 1867.455 | 2100.281 | 1865588 | 2098830
Classic-McEliece-8192128f | | | | | |
keygen | 1 | 3.442 | 3442000.000 | 0.000 | 3442000128 | 0
encaps | 469 | 3.002 | 6400.853 | 4390.492 | 6373134 | 4052869
decaps | 2316 | 3.000 | 1295.337 | 710.150 | 1293178 | 704643
HQC-128 | | | | | |
keygen | 2130 | 3.000 | 1408.451 | 810.335 | 1405164 | 808218
encaps | 1828 | 3.000 | 1641.138 | 825.134 | 1640044 | 822672
decaps | 1451 | 3.000 | 2067.540 | 807.179 | 2066850 | 806809
HQC-192 | | | | | |
keygen | 1155 | 3.001 | 2598.268 | 1082.564 | 2597403 | 1078637
encaps | 762 | 3.002 | 3939.633 | 1206.513 | 3938320 | 1206989
decaps | 575 | 3.004 | 5224.348 | 1477.989 | 5220870 | 1478511
HQC-256 | | | | | |
keygen | 869 | 3.000 | 3452.244 | 1277.074 | 3451093 | 1276135
encaps | 461 | 3.000 | 6507.592 | 1413.618 | 6498915 | 1413638
decaps | 340 | 3.001 | 8826.471 | 2130.149 | 8811765 | 2132347
Kyber512 | | | | | |
keygen | 6438 | 3.000 | 465.983 | 791.188 | 465362 | 788802
encaps | 8706 | 3.000 | 344.590 | 650.525 | 342982 | 646595
decaps | 16524 | 3.000 | 181.554 | 468.012 | 181191 | 467635
Kyber768 | | | | | |
keygen | 5664 | 3.000 | 529.661 | 757.614 | 528778 | 757648
encaps | 7003 | 3.000 | 428.388 | 635.568 | 426674 | 631767
decaps | 9763 | 3.000 | 307.283 | 499.939 | 306770 | 499331
Kyber1024 | | | | | |
keygen | 4697 | 3.000 | 638.706 | 777.571 | 637854 | 777722
encaps | 6044 | 3.000 | 496.360 | 683.177 | 494375 | 677327
decaps | 6981 | 3.000 | 429.738 | 576.330 | 428878 | 576225
Kyber512-90s | | | | | |
keygen | 5609 | 3.001 | 535.033 | 887.842 | 534676 | 887858
encaps | 7561 | 3.000 | 396.773 | 683.828 | 395450 | 680330
decaps | 13968 | 3.000 | 214.777 | 450.251 | 213774 | 440445
Kyber768-90s | | | | | |
keygen | 4659 | 3.000 | 643.915 | 798.724 | 641554 | 791589
encaps | 5369 | 3.000 | 558.763 | 729.307 | 556901 | 726899
decaps | 7020 | 3.000 | 427.350 | 551.603 | 426638 | 551509
Kyber1024-90s | | | | | |
keygen | 3627 | 3.000 | 827.130 | 778.929 | 826854 | 779045
encaps | 4523 | 3.000 | 663.277 | 694.276 | 660845 | 692613
decaps | 4883 | 3.000 | 614.376 | 586.361 | 613352 | 586562
NTRU-HPS-2048-509 | | | | | |
keygen | 99 | 3.034 | 30646.465 | 5222.935 | 30646466 | 5222937
encaps | 225 | 3.003 | 13346.667 | 3900.405 | 13342222 | 3902504
decaps | 6325 | 3.002 | 474.625 | 560.516 | 472885 | 556188
NTRU-HPS-2048-677 | | | | | |
keygen | 85 | 3.011 | 35423.529 | 8228.182 | 35423530 | 8228180
encaps | 203 | 3.002 | 14788.177 | 4131.992 | 14788178 | 4131985
decaps | 3482 | 3.000 | 861.574 | 619.258 | 860712 | 619761
NTRU-HPS-4096-821 | | | | | |
keygen | 47 | 3.013 | 64106.383 | 8942.450 | 64106382 | 8942463
encaps | 158 | 3.000 | 18987.342 | 4224.682 | 18987342 | 4224678
decaps | 2995 | 3.000 | 1001.669 | 776.747 | 1000000 | 773089
NTRU-HPS-4096-1229 | | | | | |
keygen | 25 | 3.071 | 122840.000 | 14371.305 | 122840003 | 14371313
encaps | 108 | 3.037 | 28120.370 | 5427.498 | 28120370 | 5427499
decaps | 1190 | 3.000 | 2521.008 | 826.542 | 2521008 | 826544
NTRU-HRSS-701 | | | | | |
keygen | 80 | 3.014 | 37675.000 | 7690.538 | 37662499 | 7684305
encaps | 476 | 3.009 | 6321.429 | 2261.210 | 6313026 | 2256810
decaps | 3406 | 3.000 | 880.799 | 528.574 | 879918 | 528652
NTRU-HRSS-1373 | | | | | |
keygen | 22 | 3.132 | 142363.636 | 10364.035 | 142363625 | 10363997
encaps | 213 | 3.009 | 14126.761 | 3115.596 | 14117370 | 3111448
decaps | 829 | 3.001 | 3620.024 | 1070.105 | 3616406 | 1061448
ntrulpr653 | | | | | |
keygen | 248 | 3.004 | 12112.903 | 3244.623 | 12108870 | 3245391
encaps | 1869 | 3.003 | 9869847016432618.000 | 426578396169597376.000 | 3902604 | 81887207
decaps | 1108 | 3.002 | 2709.386 | 1022.501 | 2705776 | 1023240
ntrulpr761 | | | | | |
keygen | 175 | 3.005 | 17171.429 | 4777.840 | 17171428 | 4777839
encaps | 1251 | 3.000 | 2398.082 | 1110.863 | 2389289 | 1104252
decaps | 833 | 3.001 | 3602.641 | 1291.525 | 3601441 | 1287890
ntrulpr857 | | | | | |
keygen | 144 | 3.010 | 20902.778 | 6880.245 | 20902779 | 6880245
encaps | 925 | 3.002 | 3245.405 | 892.790 | 3244325 | 892480
decaps | 781 | 3.003 | 3845.070 | 1091.378 | 3841229 | 1090240
ntrulpr1277 | | | | | |
keygen | 105 | 3.004 | 28609.524 | 6371.806 | 28609524 | 6371803
encaps | 446 | 3.002 | 6730.942 | 1571.690 | 6724216 | 1572662
decaps | 315 | 3.009 | 9552.381 | 2082.341 | 9549206 | 2082420
sntrup653 | | | | | |
keygen | 44 | 3.028 | 68818.182 | 12579.335 | 68795456 | 12572651
encaps | 261 | 3.003 | 11505.747 | 3519.645 | 11501915 | 3516376
decaps | 1459 | 3.000 | 2056.203 | 824.318 | 2054147 | 823215
sntrup761 | | | | | |
keygen | 38 | 3.070 | 80789.474 | 10283.246 | 80789477 | 10283229
encaps | 193 | 3.001 | 15549.223 | 3499.654 | 15544041 | 3504164
decaps | 905 | 3.001 | 3316.022 | 818.041 | 3312707 | 817291
sntrup857 | | | | | |
keygen | 29 | 3.054 | 105310.345 | 11086.193 | 105310349 | 11086207
encaps | 207 | 3.008 | 14531.401 | 3994.892 | 14526570 | 3999761
decaps | 748 | 3.004 | 4016.043 | 1063.371 | 4012032 | 1064050
sntrup1277 | | | | | |
keygen | 14 | 3.046 | 217571.429 | 37419.846 | 217571438 | 37419880
encaps | 76 | 3.037 | 39960.526 | 20160.175 | 39960529 | 20160191
decaps | 152 | 3.009 | 19796.053 | 10483.282 | 19782895 | 10464175
LightSaber-KEM | | | | | |
keygen | 3377 | 3.001 | 888.659 | 1653.071 | 886882 | 1652052
encaps | 7826 | 3.000 | 383.338 | 742.282 | 381293 | 738853
decaps | 9503 | 3.000 | 315.690 | 648.534 | 315269 | 647926
Saber-KEM | | | | | |
keygen | 3197 | 3.000 | 938.380 | 1008.078 | 937441 | 1007551
encaps | 4210 | 3.000 | 712.589 | 856.474 | 711401 | 853996
decaps | 4391 | 3.001 | 683.443 | 1022.853 | 680938 | 1021298
FireSaber-KEM | | | | | |
keygen | 2635 | 3.000 | 1138.520 | 1359.546 | 1138140 | 1359728
encaps | 3185 | 3.000 | 941.915 | 758.055 | 940345 | 757730
decaps | 3571 | 3.000 | 840.101 | 571.834 | 838421 | 566441
FrodoKEM-640-AES | | | | | |
keygen | 158 | 3.002 | 19000.000 | 3289.800 | 19000001 | 3289802
encaps | 185 | 3.011 | 16275.676 | 3823.781 | 16270271 | 3795082
decaps | 150 | 3.017 | 20113.333 | 3677.928 | 20113335 | 3677934
FrodoKEM-640-SHAKE | | | | | |
keygen | 246 | 3.015 | 12256.098 | 2360.641 | 12252033 | 2361940
encaps | 187 | 3.000 | 16042.781 | 5568.080 | 16037432 | 5570514
decaps | 228 | 3.008 | 13192.982 | 3664.379 | 13184211 | 3637198
FrodoKEM-976-AES | | | | | |
keygen | 79 | 3.008 | 38075.949 | 5950.794 | 38075949 | 5950788
encaps | 70 | 3.020 | 43142.857 | 8690.365 | 43142857 | 8690355
decaps | 72 | 3.013 | 41847.222 | 7398.686 | 41847221 | 7398679
FrodoKEM-976-SHAKE | | | | | |
keygen | 110 | 3.019 | 27445.455 | 4862.821 | 27427272 | 4845726
encaps | 100 | 3.012 | 30120.000 | 6624.621 | 30120000 | 6624622
decaps | 97 | 3.016 | 31092.784 | 4907.563 | 31082475 | 4906697
FrodoKEM-1344-AES | | | | | |
keygen | 39 | 3.005 | 77051.282 | 10265.573 | 77051280 | 10265575
encaps | 38 | 3.080 | 81052.632 | 13258.458 | 81052632 | 13258458
decaps | 39 | 3.053 | 78282.051 | 8808.355 | 78282050 | 8808360
FrodoKEM-1344-SHAKE | | | | | |
keygen | 68 | 3.019 | 44397.059 | 9672.793 | 44397056 | 9672790
encaps | 51 | 3.031 | 59431.373 | 9280.498 | 59431369 | 9280522
decaps | 52 | 3.052 | 58692.308 | 7921.583 | 58692308 | 7921569
Ended at 2022-04-28 05:53:13
Thanks for the explanations and test-run, @DevelopDaily ! At first glance this looks like 5-15% of native performance: Is that expected for WASM code? Some papers claim the gap should be smaller. Any idea how to compare this with "classic crypto" used via WASM? e.g., is there a way to compile openssl (3) for WASM? We could then run classic and QS crypto "head-to-head"...
@baentsch
is there a way to compile openssl (3) for WASM?
Yes, but it is more difficult because openssl is quite an old-school project. I'll see what I can do...
@baentsch
... compile openssl (3) for WASM? We could then run classic and QS crypto "head-to-head".
I have built the openssl perfectly. Unfortunately, however, I am afraid a "head-to-head" comparison won't be useful because the WASM openssl speed
is excruciatingly slow (up to minutes and hours) for three reasons:
- openssl uses assembly language too. After it is disabled, that presumably makes the system very slow
- The openssl benchmark program
speed
itself may contain bugs. They may have manifested themselves spectacularly in WASM runtime. - openssl may be very old-fashioned - simply not cut out for a modern runtime like WASM
... this looks like 5-15% of native performance: Is that expected for WASM code? Some papers claim the gap should be smaller.
Performance benchmark is a very complex issue, on which I am not an expert. Here is my understanding. The near-native performance claim of WASM is based on pure C/C++ code, which must be well-written and straightforward enough to give a modern compiler a chance to apply its optimization strategy to produce high quality assembly code. It may never beat hand-crafted assembly code in some occasions, but, in most cases, it should outperform its hand-crafted counterparts. I usually refine my C/C++ as much as possible, unless I have to write assembly, as a last resort.
For pure C/C++, I did some casual benchmarking on this code:
#include <vector>
#include <algorithm>
int main()
{
std::vector<size_t> v(50000000, 0);
std::sort(v.begin(), v.end());
return 0;
}
Native build and run:
c++ -O3 sort.cpp
time ./a.out
real 0m5.090s
user 0m3.870s
sys 0m0.678s
WASM build and run:
emcc -O3 -s TOTAL_MEMORY=167772160 -s ALLOW_MEMORY_GROWTH=1 sort.cpp
time node a.out.js
real 0m1.496s
user 0m0.906s
sys 0m0.367s
As you can see, the WASM run is actually much faster.
openssl speed is excruciatingly slow
No question, if it runs through all algorithms. What I meant to ask is: What is the relative WASM performance of functionally comparable algorithms, say for signatures, i.e., openssl speed -seconds 5 rsa
compared to openssl speed -seconds 5 dilithium3
?
openssl uses assembly language too. After it is disabled, that presumably makes the system very slow
If you use the commands above, it should allow you to compare non-assembly crypto implementations (classic vs. QS) with the same testing overhead in both cases.
The openssl benchmark program speed itself may contain bugs.
It might -- but this is highly improbable as this is pretty "seasoned" code as you also point out. It surely has not been written with WASM in mind -- but so did none of the QS algorithms.
As you can see, the WASM run is actually much faster.
Agreed -- but then again this sample basically runs two, probably differently optimized library functions against each other, not real, external code as the two commands suggested above would do.
@baentsch
If you use the commands above, it should allow you to compare non-assembly crypto implementations
WASM run:
node openssl speed -seconds 5 rsa
RSA sign setup failure. No RSA sign will be done.
00000000:error:020C0100:rsa routines:rsa_ossl_private_encrypt:malloc failure:crypto/rsa/rsa_ossl.c:265:
00000000:error:1C880004:Provider routines:rsa_sign:RSA lib:providers/implementations/signature/rsa_sig.c:652:
RSA verify setup failure. No RSA verify will be done.
version: 3.1.0-dev
built on: Tue May 3 02:42:02 2022 UTC
options: bn(32,32)
compiler: /home/t/emsdk/upstream/emscripten/emcc -Wall -O3 -DOPENSSL_USE_NODELETE -DOPENSSL_BUILDING_OPENSSL -DNDEBUG -DOPENSSL_SYS_NETWARE -DSIG_DFL=0 -DSIG_IGN=0 -DHAVE_FORK=0 -DNO_SYSLOG
CPUINFO: N/A
node openssl speed -seconds 5 dilithium3
speed: Unknown algorithm dilithium3
Native run:
./openssl speed -seconds 5 rsa
Doing 512 bits private rsa's for 5s: 47362 512 bits private RSA's in 4.92s
Doing 512 bits public rsa's for 5s: 659252 512 bits public RSA's in 4.88s
Doing 1024 bits private rsa's for 5s: 12780 1024 bits private RSA's in 4.91s
Doing 1024 bits public rsa's for 5s: 221195 1024 bits public RSA's in 4.92s
Doing 2048 bits private rsa's for 5s: 2504 2048 bits private RSA's in 4.93s
Doing 2048 bits public rsa's for 5s: 67720 2048 bits public RSA's in 4.95s
Doing 3072 bits private rsa's for 5s: 615 3072 bits private RSA's in 4.92s
Doing 3072 bits public rsa's for 5s: 29578 3072 bits public RSA's in 4.92s
Doing 4096 bits private rsa's for 5s: 271 4096 bits private RSA's in 4.95s
Doing 4096 bits public rsa's for 5s: 17182 4096 bits public RSA's in 4.90s
Doing 7680 bits private rsa's for 5s: 34 7680 bits private RSA's in 4.99s
Doing 7680 bits public rsa's for 5s: 5108 7680 bits public RSA's in 4.95s
Doing 15360 bits private rsa's for 5s: 6 15360 bits private RSA's in 5.49s
Doing 15360 bits public rsa's for 5s: 1295 15360 bits public RSA's in 4.91s
version: 3.1.0-dev
built on: Sun May 1 18:35:59 2022 UTC
options: bn(64,64)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -O3 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DNDEBUG
CPUINFO: OPENSSL_ia32cap=0xdef8220b078bffff:0x21
sign verify sign/s verify/s
rsa 512 bits 0.000104s 0.000007s 9626.4 135092.6
rsa 1024 bits 0.000384s 0.000022s 2602.9 44958.3
rsa 2048 bits 0.001969s 0.000073s 507.9 13680.8
rsa 3072 bits 0.008000s 0.000166s 125.0 6011.8
rsa 4096 bits 0.018266s 0.000285s 54.7 3506.5
rsa 7680 bits 0.146765s 0.000969s 6.8 1031.9
rsa 15360 bits 0.915000s 0.003792s 1.1 263.7
./openssl speed -seconds 5 dilithium3
speed: Unknown algorithm dilithium3
Both tests failed on the WASM openssl. If you have some other specific commands to run, I can do it. I update the docker file and you can build your own as well.
Dockerfile
FROM emscripten/emsdk:latest
RUN apt -y update
RUN apt -y install astyle cmake gcc ninja-build libssl-dev python3-pytest python3-pytest-xdist unzip xsltproc doxygen graphviz python3-yaml
RUN git clone https://github.com/open-quantum-safe/liboqs.git
RUN git clone https://github.com/openssl/openssl.git
In the docker image, follow this procedure to build:
cd /src/liboqs/
mkdir build && cd build
emcmake cmake -GNinja -DOQS_USE_OPENSSL=OFF -DOQS_PERMIT_UNSUPPORTED_ARCHITECTURE=ON ..
emmake ninja
cd /src/openssl/
emconfigure ./Configure linux-generic64 -no-asm -no-threads -static -no-afalgeng -DOPENSSL_SYS_NETWARE -DSIG_DFL=0 -DSIG_IGN=0 -DHAVE_FORK=0 -DNO_SYSLOG
sed -i 's|^CROSS_COMPILE.*$|CROSS_COMPILE=|g' Makefile
emmake make
Performance aside, I've created a small demo of using the library in the browser: https://github.com/x0wllaar/liboqs-wasm. I did not test with Node/Bun, but I think it should work there as well.
In general, unmodified library code works, but I had to write some very boilerplate accessor methods, and then write a more idiomatic wrapper in JS.
On the performance side, for me everything I tested except SPHINCS+ worked smoothly enough to not notice it blocking the event loop. For client side, even with SPHINCS+, it can be mitigated by moving it into a worker thread. I did not test McEliece.
PS: Is it possible to enable BIKE in WASM and to add MAYO signatures? Testing BIKE was kind of a half of my inspiration for doing all this and MAYO just seems cool.
Performance aside, I've created a small demo of using the library in the browser: https://github.com/x0wllaar/liboqs-wasm. I did not test with Node/Bun, but I think it should work there as well.
In general, unmodified library code works, but I had to write some very boilerplate accessor methods, and then write a more idiomatic wrapper in JS.
On the performance side, for me everything I tested except SPHINCS+ worked smoothly enough to not notice it blocking the event loop. For client side, even with SPHINCS+, it can be mitigated by moving it into a worker thread. I did not test McEliece.
PS: Is it possible to enable BIKE in WASM and to add MAYO signatures? Testing BIKE was kind of a half of my inspiration for doing all this and MAYO just seems cool.
Thanks, looks interesting.
MAYO is being worked on in #1707 and will hopefully land in liboqs soon. BIKE is already available. We have two BIKE implementation versions -- a generic C for 64-bit little endian, and an x86_64 optimized implementation. I'm not sure how that would interact with the constraints of WASM.
64-bit little endian
Yeah that's the problem as by default Emscripten compiles for 32-bit WASM. The 64-bit version is not really supported right now (see https://webassembly.org/features/, specifically Memory64 there).
Emscripten supports (https://emscripten.org/docs/tools_reference/settings_reference.html?highlight=environment#memory64) "faking it" by compiling to 64-bit and then lowering to 32, I'll see if/how well it works for liboqs.
From the JS side, the only change would be to add a way to determine the size of size_t
at runtime (currently hardcoded to 4 in the wrapper), and then just make sure that BigInt pointers are interpreted correctly (for null checks etc). Both of these seem very easy to do for me.
I'll try to do it by the end of this week.
Among other things to address is the 16mb stack size, which is kinda necessary for McEliece to work, but I'll see how much I can lower it and still have it working. Is the large stack still required when using heap-allocated versions of KEMs/Signatures?
After some fighting with CMake, I managed to get BIKE to work in browsers without any experimental options, thank you!
After some fighting with CMake, I managed to get BIKE to work in browsers without any experimental options, thank you!
Thanks for letting us know @x0wllaar . If there'd be something you learned that may benefit other users of liboqs
please feel free to share, e.g., by PR to the cmake option documentation.