{"id":31179486,"url":"https://github.com/dchest/mldsa-wasm","last_synced_at":"2025-09-19T15:22:47.455Z","repository":{"id":311800468,"uuid":"1045164416","full_name":"dchest/mldsa-wasm","owner":"dchest","description":"ML-DSA-65 postquantum signatures in WebAssembly","archived":false,"fork":false,"pushed_at":"2025-08-26T18:44:33.000Z","size":104,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-26T23:57:31.241Z","etag":null,"topics":["cryptography","ml-dsa","ml-dsa-65","postquantum","signatures","webcrypto"],"latest_commit_sha":null,"homepage":"https://dchest.github.io/mldsa-wasm/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dchest.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-26T18:43:43.000Z","updated_at":"2025-08-26T19:23:39.000Z","dependencies_parsed_at":"2025-08-26T23:57:36.568Z","dependency_job_id":"fedb4440-05b0-414e-b418-3a57806e6947","html_url":"https://github.com/dchest/mldsa-wasm","commit_stats":null,"previous_names":["dchest/mldsa-wasm"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/dchest/mldsa-wasm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dchest%2Fmldsa-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dchest%2Fmldsa-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dchest%2Fmldsa-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dchest%2Fmldsa-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dchest","download_url":"https://codeload.github.com/dchest/mldsa-wasm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dchest%2Fmldsa-wasm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275958519,"owners_count":25559736,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-19T02:00:09.700Z","response_time":108,"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":["cryptography","ml-dsa","ml-dsa-65","postquantum","signatures","webcrypto"],"created_at":"2025-09-19T15:22:45.484Z","updated_at":"2025-09-19T15:22:47.445Z","avatar_url":"https://github.com/dchest.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mldsa-wasm\n\n**ML-DSA-65, a post-quantum digital signature algorithm in WebAssembly.**\n\nThis package provides a WebAssembly-based implementation of ML-DSA-65, based on [PQClean](https://github.com/PQClean/PQClean). It exposes a modern, WebCrypto-compatible API for key generation, signing, and verification, all bundled in a single JavaScript file with the WASM module inlined.\n\n## Features\n\n- API compatible with the [WebCrypto API draft for modern algorithms](https://wicg.github.io/webcrypto-modern-algos/) (when it ships, replace `mldsa` with `crypto.subtle` and burn this package).\n- All code and WASM are bundled into a single `dist/mldsa.js` ES module (no external `.wasm` files needed).\n- Works in browsers and Node.js, and should work everywhere WebAssembly is supported.\n- Smallish: 63 KB unminified .js (21 KB gzipped / 17 KB brotlied).\n- Fast: signing at 2800 ops/sec, verifying at 10500 ops/sec on MacBook Air M1.\n- A single, most common ML-DSA-65 algorithm, so there's no need to choose between 44, 65, and 87!\n\nUse it as a stopgap solution until the [WebCrypto API supports ML-DSA natively](https://wicg.github.io/webcrypto-modern-algos/).\n\nDemo: \u003chttps://dchest.github.io/mldsa-wasm/\u003e\n\n\u003e [!CAUTION]\n\u003e Beta version. CONTAINS CRYPTOGRAPHY! Use at your own risk.\n\n## Limitations\n\n- The `CryptoKey` returned by this module's `generateKey` and `importKey` has the same prototype as WebCrypto's `CryptoKey`, but cannot be cloned with `structuredClone`, so you cannot, for example, save them to IndexedDB, pass them to a worker, or use `wrapKey` on them, without exporting. You can only use them with this library's methods. Cloning is deliberately disabled to prevent compatibility issues with the future WebCrypto API (e.g., you saved an `mldsa-wasm` key to IndexedDB, and then switched to the native WebCrypto API, which has its own internal key format and cannot deserialize it).\n- Key material is not accessible from outside of the module (that is, you should not be able to get raw key data without exporting), but is somewhere in JavaScript memory until garbage collected. The module takes care to wipe key data from memory during garbage collection, but JavaScript runtimes may optimize this cleanup away.\n- Operations, while asynchronous on the surface (all functions are `async` to be compatible and to be able to load the WASM module without a separate initialization call), are done synchronously, instead of being fully asynchronous like in the WebCrypto API. You may consider it an improvement.\n- Base64 encoding and decoding for JWK is not constant-time (not sure if it is in other implementations except BoringSSL, though).\n- `pkcs8` import only supports the seed format of private keys (as nature intended).\n\n## Installation\n\n```sh\nnpm install mldsa-wasm\n```\n\n## Usage Example\n\n### Signing and verifying\n\n```js\nimport mldsa from \"mldsa-wasm\";\n\n// Generate key pair\nconst keyPair = await mldsa.generateKey({ name: \"ML-DSA-65\" }, true, [\n  \"sign\",\n  \"verify\",\n]);\nconst { publicKey, privateKey } = keyPair;\n\n// Sign a message\nconst message = new TextEncoder().encode(\"Hello, world!\");\n\nconst signature = await mldsa.sign({ name: \"ML-DSA-65\" }, privateKey, message);\n\n// Verify a signature\nconst isValid = await mldsa.verify(\n  { name: \"ML-DSA-65\" },\n  publicKey,\n  signature,\n  message\n);\n\nconsole.log(\"Signature is valid:\", isValid); // true\n\n// Sign a message with context (maximum 255 bytes)\nconst context = new TextEncoder().encode(\"MyApp v1.0\");\nconst signatureWithContext = await mldsa.sign(\n  { name: \"ML-DSA-65\", context },\n  privateKey,\n  message\n);\n\n// Verify a signature with context\nconst isValidWithContext = await mldsa.verify(\n  { name: \"ML-DSA-65\", context },\n  publicKey,\n  signatureWithContext,\n  message\n);\n```\n\n### Exporting and importing keys\n\nYou can export and import ML-DSA keys in several formats. Here are some examples:\n\n#### Exporting a public key (raw format)\n\n```js\n// Export public key as raw bytes\nconst rawPublicKey = await mldsa.exportKey(\"raw-public\", publicKey);\n// rawPublicKey is an ArrayBuffer\n```\n\n#### Exporting a private key (seed format)\n\n```js\n// Export private key as a seed\nconst rawSeed = await mldsa.exportKey(\"raw-seed\", privateKey);\n// rawSeed is an ArrayBuffer\n```\n\n#### Exporting a key as JWK\n\n```js\n// Export public key as JWK\nconst jwkPublic = await mldsa.exportKey(\"jwk\", publicKey);\n// jwkPublic is a JsonWebKey object\n```\n\n#### Importing a public key (raw format)\n\n```js\n// Import a public key from raw bytes\nconst importedPublicKey = await mldsa.importKey(\n  \"raw-public\",\n  rawPublicKey,\n  { name: \"ML-DSA-65\" },\n  true, // extractable\n  [\"verify\"]\n);\n```\n\n#### Importing a private key (seed format)\n\n```js\n// Import a private key from seed\nconst importedPrivateKey = await mldsa.importKey(\n  \"raw-seed\",\n  rawSeed,\n  { name: \"ML-DSA-65\" },\n  false, // not extractable\n  [\"sign\"]\n);\n```\n\n#### Importing a key from JWK\n\n```js\n// Import a public key from JWK\nconst importedJwkPublicKey = await mldsa.importKey(\n  \"jwk\",\n  jwkPublic,\n  { name: \"ML-DSA-65\" },\n  false,\n  [\"verify\"]\n);\n```\n\nSPKI and PKCS8 formats are also supported.\n\n## API Reference\n\nAll API methods are asynchronous and return Promises. See [Modern Algorithms in the Web Cryptography API](https://wicg.github.io/webcrypto-modern-algos/) for details.\n\n### `mldsa.generateKey(algorithm, extractable, usages)`\n\n- **algorithm**: `{ name: \"ML-DSA-65\" }` or `\"ML-DSA-65\"`\n- **extractable**: `boolean` (for private key)\n- **usages**: array of usages: `\"sign\"`, `\"verify\"`\n- **Returns**: `{ publicKey, privateKey }` (both are `CryptoKey`)\n\n### `mldsa.exportKey(format, key)`\n\n- **format**: `\"raw-public\"`, `\"raw-seed\"`, `\"jwk\"`, `\"pkcs8\"` or `\"spki\"`\n- **key**: `CryptoKey`\n- **Returns**: `ArrayBuffer` or `JsonWebKey`\n\n### `mldsa.importKey(format, keyData, algorithm, extractable, usages)`\n\n- **format**: `\"raw-public\"`, `\"raw-seed\"`, `\"jwk\"`, `\"pkcs8\"` or `\"spki\"`\n- **keyData**: `ArrayBuffer`, typed array, or `JsonWebKey`\n- **algorithm**: `{ name: \"ML-DSA-65\" }` or `\"ML-DSA-65\"`\n- **extractable**: `boolean`\n- **usages**: array of usages\n- **Returns**: `CryptoKey`\n\n### `mldsa.sign(algorithm, key, data)`\n\n- **algorithm**: `{ name: \"ML-DSA-65\", context? }` or `\"ML-DSA-65\"`\n- **key**: private `CryptoKey`\n- **data**: `ArrayBuffer` or typed array (data to sign)\n- **Returns**: `ArrayBuffer` (signature)\n\n### `mldsa.verify(algorithm, key, signature, data)`\n\n- **algorithm**: `{ name: \"ML-DSA-65\", context? }` or `\"ML-DSA-65\"`\n- **key**: public `CryptoKey`\n- **signature**: `ArrayBuffer` or typed array\n- **data**: `ArrayBuffer` or typed array (original data)\n- **Returns**: `boolean` (true if signature is valid)\n\n### `mldsa.getPublicKey(key, usages)`\n\n- **key**: private `CryptoKey`\n- **usages**: array of usages for the returned public key (`\"verify\"`)\n- **Returns**: public `CryptoKey`\n\n### `mldsa._isSupportedCryptoKey(key)`\n\nNon-spec method to check if a `CryptoKey` was created by this library.\nYou can use it to distinguish WebCrypto's native keys from `mldsa-wasm` keys.\n\n- **key**: `CryptoKey`\n- **Returns**: `boolean`\n\n### Types\n\n- `CryptoKey`: Internal key object, not compatible with WebCrypto's `CryptoKey`.\n- Usages: `\"sign\"`, `\"verify\"`\n- Formats: `\"raw-public\"`, `\"raw-seed\"`, `\"jwk\"`, `\"pkcs8\"`, `\"spki\"`\n\n## When WebCrypto API ships\n\nOnce the WebCrypto API supports ML-DSA natively (assuming the draft ships as-is), just switch `mldsa` to `crypto.subtle` and use the native API directly.\n\n## Spec changes\n\nSince the WebCrypto API draft is still evolving, this library may need updates to keep up with changes in the spec. The updates are not guaranteed (but I will try to keep up), and they may break compatibility with previous versions.\n\n## Build Instructions\n\n### Prerequisites\n\n- [Emscripten](https://emscripten.org/) (for building WASM)\n- `npm install` to install dev dependencies (`esbuild`, `typescript`, and `vitest`).\n\n### Build\n\n- Run:\n  ```sh\n  npm run build\n  ```\n- This uses Emscripten to compile C sources, which puts the result into `src/build/wasm-module.js` (WASM inlined as base64).\n- Creates a single distributable file by combining `src/build/wasm-module.js` and `src/mldsa.ts` using `esbuild`, resulting in `dist/mldsa.js`.\n- Creates TypeScript types in `types/mldsa.d.ts` by running `tsc`.\n\nNote: we have an internal copy of PQClean's `ml-dsa-65` implementation, modified to accept\nseeds instead of calling `randombytes()`. We generate seeds using `crypto.getRandomValues()`\nand pass them to WebAssembly for key generation and signing.\n\n## Distribution\n\n- The entire library is distributed as a single-file ES module: `dist/mldsa.js`.\n- The WASM module is inlined as base64, so no external files are needed.\n- TypeScript types are in `types/mldsa.d.ts`.\n\n## License\n\n- WASM wrapper: MIT License\n- PQClean: See [pqclean/LICENSE](src/pqclean/LICENSE) (Public Domain)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdchest%2Fmldsa-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdchest%2Fmldsa-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdchest%2Fmldsa-wasm/lists"}