{"id":51121799,"url":"https://github.com/tegmentum/openssl-wasm","last_synced_at":"2026-06-25T03:30:30.819Z","repository":{"id":361728930,"uuid":"1220257245","full_name":"tegmentum/openssl-wasm","owner":"tegmentum","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-11T14:03:56.000Z","size":447,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T16:06:55.853Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tegmentum.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-24T18:00:45.000Z","updated_at":"2026-06-11T14:14:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tegmentum/openssl-wasm","commit_stats":null,"previous_names":["tegmentum/openssl-wasm"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tegmentum/openssl-wasm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fopenssl-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fopenssl-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fopenssl-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fopenssl-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tegmentum","download_url":"https://codeload.github.com/tegmentum/openssl-wasm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tegmentum%2Fopenssl-wasm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34758773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-25T03:30:29.423Z","updated_at":"2026-06-25T03:30:30.813Z","avatar_url":"https://github.com/tegmentum.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# openssl-wasm\n\n[![CI](https://github.com/tegmentum/openssl-wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/tegmentum/openssl-wasm/actions/workflows/ci.yml)\n[![Bench](https://github.com/tegmentum/openssl-wasm/actions/workflows/bench.yml/badge.svg)](https://github.com/tegmentum/openssl-wasm/actions/workflows/bench.yml)\n[![Fuzz](https://github.com/tegmentum/openssl-wasm/actions/workflows/fuzz.yml/badge.svg)](https://github.com/tegmentum/openssl-wasm/actions/workflows/fuzz.yml)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)\n\nOpenSSL 3.6.2 compiled as a WebAssembly Component (target `wasm32-wasip2`),\ncallable from a `wasmtime` host via a curated WIT surface.\n\nThe component is **real OpenSSL** — `third_party/openssl` is the upstream\nsource, built with `wasi-sdk` 33's clang into `libcrypto.a` + `libssl.a`\nand statically linked into a wasm component. The C glue in `src/` maps\nthe WIT exports to OpenSSL's `EVP_*`, `X509_*`, `SSL_*`, etc. The only\nRust in the repo is the test driver in `examples/host`.\n\n## Status\n\n- All 256 WIT exports are implemented against real OpenSSL.\n- 106 automated tests in `examples/host/tests/` pass (plus 1\n  opt-in `#[ignore]` slow-path test), including NIST/RFC/FIPS 197\n  known-answer vectors.\n- TLS client verified live against `example.com:443` with real\n  certificate-chain validation against the system CA bundle.\n\n## Interfaces (see `wit/`)\n\n| Interface | Covers |\n|-----------|--------|\n| `error`   | ERR_get_error_all, describe, drain |\n| `random`  | RAND_bytes / RAND_priv_bytes / RAND_add |\n| `digest`  | MD5/SHA-1/SHA-2/SHA-3/SHAKE/BLAKE2/RIPEMD/SM3 one-shot + streaming + XOF |\n| `mac`     | HMAC / CMAC / KMAC / Poly1305 / SipHash / GMAC |\n| `cipher`  | AES (ECB/CBC/CTR/CFB/OFB/XTS/GCM/CCM/OCB), ChaCha20-Poly1305, Camellia, ARIA, 3DES, RC4 |\n| `kdf`     | PBKDF2, HKDF, scrypt, Argon2, KBKDF, SSKDF, X9.63, TLS1.3-expand-label |\n| `pkey`    | RSA / RSA-PSS / EC / Ed25519,Ed448 / X25519,X448 / DH (+ named RFC 7919 / RFC 3526 groups) — keygen, sign/verify, encrypt/decrypt, derive, PEM/DER/PKCS#8 |\n| `x509`    | Certificates / CSRs / CRLs / Store / chain validation / PKCS#12 / CMS |\n| `tls`     | TLS 1.2 / 1.3 client and server, ALPN, SNI, session tickets, keylog |\n| `bignum`  | BIGNUM arithmetic |\n\n## Layout\n\n```\n.\n├── config/50-wasm.conf        # OpenSSL Configure target for wasm32-wasip2\n├── scripts/\n│   ├── install-wasi-sdk.sh    # downloads wasi-sdk 33 into .wasi-sdk/\n│   └── gen-stubs.sh           # regenerates src/stubs.c (should be empty now)\n├── third_party/openssl/       # submodule, pinned to openssl-3.6.2\n├── wit/                       # Component Model interface definitions\n│   ├── world.wit              # re-exports everything below\n│   ├── error.wit random.wit bignum.wit digest.wit mac.wit\n│   ├── cipher.wit kdf.wit pkey.wit x509.wit tls.wit\n├── src/                       # C glue: WIT exports → OpenSSL calls\n│   ├── component.c            # error, random, digest (+ streaming)\n│   ├── mac.c kdf.c bignum.c cipher.c pkey.c\n│   ├── x509.c                 # parse / info / verify / store / PKCS#12 / CMS sign+verify\n│   ├── x509_build.c           # cert \u0026 CSR build-and-sign, PKCS#12 build, CMS encrypt/decrypt\n│   ├── tls.c                  # client + server\n│   └── include/               # shared helpers (support.h, algs.h)\n├── examples/host/             # Rust wasmtime harness\n│   ├── src/lib.rs             # Fixture + bindgen\n│   ├── src/main.rs            # demo runner\n│   └── tests/*.rs             # 22 suites, 106 tests total (+1 ignored)\n├── .github/workflows/ci.yml   # build + test on every push\n└── Makefile\n```\n\n## Prerequisites\n\n- **wasi-sdk 33.** `scripts/install-wasi-sdk.sh` drops it into `.wasi-sdk/`\n  (gitignored). The Makefile picks that up automatically.\n- **wasm-tools** and **wit-bindgen** (both from cargo). Tested with\n  wasm-tools 1.247 and wit-bindgen 0.48.\n- **wasmtime** runtime for the Rust host (45.x tested).\n- **cargo / rustc** stable.\n\n## Build\n\n```sh\ngit submodule update --init --recursive\nscripts/install-wasi-sdk.sh     # one-time, 200 MB download\nmake                            # → build/openssl-component.wasm (~3.8 MB)\nmake test                       # runs the full cargo test suite\nmake run                        # runs the demo binary against the component\n```\n\nOverride wasi-sdk location: `WASI_SDK=/path/to/wasi-sdk make`.\n\n## Using the component\n\nFrom Rust with wasmtime 45:\n\n```rust\nwasmtime::component::bindgen!({\n    world: \"openssl\",\n    path: \"wit\",\n    imports: { default: async },\n    exports: { default: async },\n});\n\n// … instantiate, then call bindings.openssl_component_digest().call_one_shot(…)\n```\n\nSee `examples/host/src/main.rs` and `examples/host/tests/*.rs` for\nworked examples.\n\nFor TLS with certificate verification, grant the component `inherit_network()`,\n`allow_ip_name_lookup(true)`, and preopen a directory containing a CA\nbundle (`/etc/ssl/cert.pem` on macOS,\n`/etc/ssl/certs/ca-certificates.crt` on Debian/Ubuntu).\n\n## Design notes\n\n- **Target:** `wasm32-wasip2`. wasi-sdk 33's clang emits a Component\n  directly (magic bytes `0d 00 01 00`), so there's no `wasm-tools\n  component new` step. We link with `-mexec-model=reactor` so the\n  component exports only our WIT interfaces, no `wasi:cli/run`.\n- **Disables at Configure time:** `no-threads`, `no-shared`, `no-dso`,\n  `no-asm`, `no-engine`, `no-async`, `no-afalgeng`, `no-ktls`,\n  `no-ui-console`, `no-autoload-config`, `no-module`, `no-apps`,\n  `no-docs`, `no-quic`, `no-tests`, `OPENSSL_NO_UNIX_SOCK` (wasi-libc's\n  `sockaddr_un` has no `sun_path`).\n- **Sockets:** TLS is routed through wasi-libc's BSD socket shims,\n  which forward to `wasi:sockets`. The host must grant socket permissions.\n- **Filesystem:** CA bundle loading goes through `wasi:filesystem`.\n  Preopen a directory, then call `store.load-from-file`.\n- **RNG:** OpenSSL's DRBG is seeded via `getentropy`, backed by\n  `wasi:random`.\n- **Time:** certificate validity uses `wasi:clocks`.\n- **setjmp/longjmp:** wasi-sdk 33 lowers these to Wasm exception\n  handling. Wasmtime ≥ 40 has this enabled by default.\n\n## Performance\n\nMeasured on an Apple M-series, wasmtime 44, opt-level=2 (see\n`examples/host/benches/component_vs_native.rs`):\n\n| Operation                     | Component       | Native (libssl) | Ratio |\n|-------------------------------|----------------:|----------------:|------:|\n| SHA-256 (1 MiB)               |       256 MiB/s |     2,384 MiB/s |   ~9× |\n| AES-256-GCM seal (64 KiB)     |        52 MiB/s |     6,208 MiB/s | ~120× |\n| Ed25519 sign (1 KiB msg)      |          360 µs |          31.5 µs|  ~11× |\n\nInterpretation:\n- SHA-256 and signatures are within an order of magnitude of native.\n- AES-256-GCM is much slower because native uses AES-NI hardware\n  instructions; the component runs OpenSSL's portable C implementation\n  (`no-asm` at Configure).\n- If you need high AEAD throughput, consider batching or picking\n  ChaCha20-Poly1305 where both sides are pure software.\n\nRun `cargo bench --manifest-path examples/host/Cargo.toml` to reproduce.\n\n### Wasm SIMD\n\n`make simd=on` turns on `-msimd128` during the OpenSSL build. Measured\neffect on this workload:\n\n| Operation                  | Baseline  | `simd=on` | Δ      |\n|----------------------------|----------:|----------:|-------:|\n| SHA-256 (1 MiB)            | 226 MiB/s | 242 MiB/s |   +7%  |\n| AES-256-GCM seal (64 KiB)  |  41 MiB/s |  41 MiB/s |   0%   |\n| Ed25519 sign               |    391 µs |    387 µs |  ~1%   |\n\nConclusion: modest SHA-256 improvement from clang's auto-vectorizer,\nno material change on AES or elliptic curves.\n\n### Wasm SIMD AES (experimental)\n\n`make simd_aes=on` replaces OpenSSL's T-table `AES_encrypt` /\n`AES_decrypt` with hand-written wasm SIMD implementations via linker\n`--wrap`. Coverage: AES-128 / 192 / 256 encrypt and decrypt. Validated\nagainst NIST FIPS-197 and CAVP ECBGFSbox / ECBKeySbox vectors plus a\n1000-round differential against native libssl.\n\n**Current state: correctness-complete but slower than T-tables.**\n\n| Operation                  | T-table   | `simd_aes=on`| Δ        |\n|----------------------------|----------:|-------------:|---------:|\n| AES-256-GCM seal (64 KiB)  |  56 MiB/s |    34 MiB/s  |  -40%    |\n| AES-256-GCM seal (1 KiB)   |  1.9 MiB/s|    1.8 MiB/s |   -2%    |\n\nWhy slower: the current S-box is a scalar 256-entry lookup (16 byte\nloads per block), surrounded by v128↔memory round-trips. T-tables\navoid this by fusing SubBytes + ShiftRows + MixColumns into a single\n4-way-indexed 32-bit table load per byte. Closing the gap requires\nreplacing the scalar S-box with a vpAES nibble-swizzle\n(Hamburg 2009) — ~12 SIMD ops per block instead of 16 scalar\nlookups. That work isn't in-tree yet.\n\nDefault: **off**. Enable only if you're iterating on the algorithm.\n\n## Security notes\n\n### Constant-time guarantees\n\nWhatever constant-time properties OpenSSL's native implementation\nprovides are preserved — the glue passes buffers through unmodified\nand doesn't perform data-dependent branching. Specifically:\n\n**Believed constant-time in OpenSSL (and preserved here):**\n- `digest.one-shot`, `digest.context.update/finish` — block-based,\n  not data-dependent.\n- `mac.*` — HMAC/CMAC/Poly1305/SipHash/GMAC all constant-time in\n  OpenSSL.\n- `cipher.*` for AES via constant-time software implementations when\n  AES-NI isn't available (OpenSSL's default on wasm).\n- `pkey.sign_*` / `pkey.verify_*` / `pkey.derive` for RSA/EC/Ed/X/DH.\n- `cipher.open` tag verification (uses `CRYPTO_memcmp`).\n\n**NOT constant-time — avoid with secret inputs:**\n- `bignum.to-dec` / `bignum.to-hex` — base conversion is data-dependent.\n- `bignum.from-dec` / `bignum.from-hex` — parsing branches on digits.\n- `error.describe` — string formatting.\n- `pkey.save-private` / `pkey.load-private` — ASN.1 encoding size\n  leaks key size bits.\n- Any RSA/EC operation on a public key that happens to be used for\n  signing (the library doesn't know your intent).\n\n### Threat model\n\n- The component runs in a wasmtime sandbox. Memory corruption in\n  OpenSSL cannot escape the sandbox.\n- The component assumes the host-provided `wasi:random` is a secure\n  CSPRNG. If the host is compromised or misconfigured (e.g., feeds\n  `/dev/zero` as entropy), all key material is compromised.\n- The component loads CA bundles via `wasi:filesystem`. A malicious\n  host can substitute the bundle, so TLS trust is only as strong as\n  the preopened directory.\n- Wasm exception handling is used for `setjmp`/`longjmp` in libssl\n  error paths. Wasmtime ≥ 40 handles this; older runtimes will trap.\n\n### What this component does NOT protect against\n\n- Timing side channels on the host CPU: the component runs on the host\n  CPU, so any data-dependent timing in OpenSSL flows through wasm\n  translation but is still observable.\n- The host can read the component's linear memory at any time. Do not\n  use this for key custody in adversarial-host scenarios.\n- The component does not implement side-channel hardening (e.g.\n  blinding) beyond what OpenSSL provides natively.\n\n## Deliberate WIT-surface omissions\n\nThe WIT surface exposes OpenSSL's curated high-level APIs. Low-level\nAPIs that don't translate cleanly to the Component Model (BIO, ASN.1,\nOSSL_PARAM, providers, engines, raw NID lookups) are not exported.\nCallers that need those should add more WIT interfaces and glue\nfunctions, rather than try to expose raw pointers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftegmentum%2Fopenssl-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftegmentum%2Fopenssl-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftegmentum%2Fopenssl-wasm/lists"}