rob thijssen 6eb04f63ac feat(quantus-crypto): ML-DSA, Poseidon2 and HD derivation as a separate crate
Wraps the chain's own crypto crates for the browser: qp-rusty-crystals-dilithium
(ML-DSA-65 and ML-DSA-87), qp-poseidon-core for the account-id hash, and
qp-rusty-crystals-hdwallet for BIP44 derivation. Nothing is reimplemented — a
browser wallet that disagreed with the chain about a key or a signature would
emit well-formed output the chain rejects, with nothing on this side able to
tell.

A separate crate rather than more files in wasm-crypto, because the two cannot
share a Cargo graph. wasm-crypto builds with nightly-2022-06-24 against a
2019-era dependency set; the ML-DSA crates use inline `const {}` blocks that
need Rust >= 1.79. Bumping the older one would mean rewriting upstream's
sr25519/ed25519 build, which is the thing most worth leaving alone so rebases
stay boring. wasm-crypto is untouched here.

The scheme selector is the chain's own signature-enum variant index (0 for
ML-DSA-87, 1 for ML-DSA-65), so the number threaded through this API is the
byte that ends up on the wire and there is no mapping to get backwards. Key and
signature sizes are exported rather than left for JS to hardcode: they are
consensus-critical and a drifted constant would mis-frame every byte after the
signature while looking healthy.

Logic is split from the #[wasm_bindgen] wrappers because JsError cannot be
constructed off-wasm, which made every error path untestable by cargo test —
and the error paths are what most needs testing.

Verified against the `quantus` CLI 2.2.2 as an independent oracle, not against
our own output: the three dev-genesis account ids, and HD derivation at both
schemes' default paths from the public Substrate dev phrase. Context separation
is pinned too — a signature made under QUANTUS_EXTRINSIC must not verify under
the empty context, which is what makes the spec-148 boundary detectable rather
than a silent chain rejection.

Refs quantus/wasm#1

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-10 13:54:25 +03:00
2025-08-13 13:59:40 -03:00
2025-01-02 21:26:31 +02:00
2020-10-23 11:14:40 +02:00
2026-03-13 01:32:10 +02:00
2019-03-17 13:12:14 +01:00
2022-07-11 16:36:57 +03:00
2022-01-13 10:05:36 +02:00
2019-03-17 13:12:14 +01:00
2021-06-08 17:04:07 +02:00
2026-03-13 01:32:10 +02:00
2025-01-02 21:26:31 +02:00
2025-12-09 15:27:27 +05:30
2019-03-15 13:00:05 +01:00
2025-12-09 10:03:06 +00:00
2026-03-13 01:32:10 +02:00
2022-08-16 15:04:17 +03:00
2019-03-17 13:12:14 +01:00
2025-12-09 10:03:06 +00:00
2022-12-08 08:21:37 +02:00
2026-03-13 01:32:10 +02:00
2025-12-09 15:27:27 +05:30

@polkadot/wasm

Various WASM wrappers around Rust crates

overview

It is split up into a number of internal packages, namely utilities -

  • wasm-crypto Various hashing functions, sr25519 & ed25519 crypto

These are split from the polkadot-js/util repo where it is heavily used as part of @polkadot/util-crypto. (There JS fallbacks are available for some interfaces, e.g. hashing, but for sr25519 WASM is the only interface). Since these don't undergo massive changes on a daily basis and has a build overhead (WASM compilation & optimisation), it is better managed as a seperate repo with a specific CI configuration.

development

Contributions are welcome!

To start off, this repo (along with others in the @polkadot family) uses yarn workspaces to organise the code. As such, after cloning, its dependencies should be installed via yarn, not via npm; the latter will result in broken dependencies.

Description
Fork of polkadot-js/wasm. Adds ML-DSA-65/87 and Poseidon2-over-Goldilocks to the @polkadot/wasm-crypto build, wrapping the chain's own qp-rusty-crystals-dilithium / qp-poseidon-core / qp-rusty-crystals-hdwallet crates so the browser and the runtime share one implementation.
Readme Apache-2.0 32 MiB
Languages
Rust 45.7%
JavaScript 25.1%
TypeScript 25%
Shell 4%
HTML 0.2%