npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@webbuf/ed25519

v3.8.0

Published

Rust/wasm Ed25519 PureEdDSA (RFC 8032) for the web, node.js, deno, and bun.

Downloads

76

Readme

@webbuf/ed25519

Ed25519 PureEdDSA digital signatures (RFC 8032) for WebBuf, optimized with Rust/WASM.

Ed25519 is the standard signature primitive used by Signal, OpenSSH, OpenPGP, age, Tor, and increasingly Web PKI. It pairs with @webbuf/x25519 for ECDH and with @webbuf/mldsa for post-quantum hybrid signatures.

Installation

npm install @webbuf/ed25519

Usage

import {
  ed25519PublicKeyCreate,
  ed25519Sign,
  ed25519Verify,
} from "@webbuf/ed25519";
import { WebBuf } from "@webbuf/webbuf";
import { FixedBuf } from "@webbuf/fixedbuf";

// Each party generates a 32-byte seed and derives the public key.
const priv = FixedBuf.fromRandom<32>(32);
const pub = ed25519PublicKeyCreate(priv);

// Sign a message.
const message = WebBuf.fromUtf8("hello, ed25519");
const signature = ed25519Sign(priv, message);

// Verify it.
const ok = ed25519Verify(pub, message, signature); // true

Seed semantics

The 32-byte privKey parameter is the seed (RFC 8032 §5.1.5 secret key) — NOT the 64-byte expanded form some libraries expose as the "secret key." The seed is what's stored on disk in OpenSSH, OpenPGP, and most consumer key formats. Internally, the seed is hashed with SHA-512 to produce the (clamped) signing scalar plus the 32-byte prefix used by the signing nonce.

ed25519-dalek 2.x's SigningKey::to_bytes() also returns the seed (not the expanded form), so round-tripping through serialization works as expected.

PureEdDSA only

This package implements PureEdDSA per RFC 8032 §5.1.6 / §5.1.7. The signer consumes the raw message bytes directly — no prehash, no Ed25519ph variant.

PureEdDSA preserves the collision-resilience guarantee that RFC 8032 calls out: even if the hash function used internally (SHA-512) had a collision, signing two distinct messages with the same key would not yield interchangeable signatures.

Consumers who want to sign a digest should hash externally and pass the digest as the message argument; the primitive itself never prehashes.

Determinism

PureEdDSA signing is deterministic per RFC 8032: the same (privKey, message) pair always produces the same signature. WebBuf does not opt into the hedged- signing variant added in ed25519-dalek 2.x (which would require pulling RNG into the WASM build). If you need hedged signing for side-channel resistance, use a different primitive or layer your own hedging on top.

Strict verification (RFC 8032 §5.1.7)

ed25519Verify returns boolean. Failed verification is not an exception:

  • Wrong key, tampered message, tampered signature, non-canonical S, small-order R, malformed point bytes — all return false.
  • Only input-length errors (private key not 32 bytes, signature not 64 bytes, etc.) throw.

The underlying Rust crate has legacy_compatibility disabled and the wrapper calls VerifyingKey::verify_strict (not the cofactored verify). That means strict RFC 8032 §5.1.7 verification is enforced: signatures with non-canonical S (i.e. S >= L) are rejected, signatures with non-canonical R are rejected, and small-order public keys are rejected. This is what most modern Ed25519 consumers expect.

Strict verification matters: without it, a malicious peer presenting the Curve25519 identity element as their public key combined with an identity-R / zero-S signature would produce a universal forgery accepting any message. WebBuf's verifier rejects this case explicitly; a regression test asserts it.

API

| Function | Description | | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | ed25519PublicKeyCreate(privKey: FixedBuf<32>): FixedBuf<32> | Derive public key from a 32-byte seed | | ed25519Sign(privKey: FixedBuf<32>, message: WebBuf): FixedBuf<64> | PureEdDSA sign — returns 64-byte (R \|\| S) signature | | ed25519Verify(pubKey: FixedBuf<32>, message: WebBuf, signature: FixedBuf<64>): boolean | PureEdDSA verify — returns true/false; throws on length errors only |

Audit posture

The curve25519-dalek and subtle crates received a security audit by Quarkslab in 2019 (commissioned by Tari Labs). That audit covered the pre-1.0 codebase. The current curve25519-dalek 4.x and ed25519-dalek 2.x lines are not under the 2019 audit, but the pinned versions (=4.1.3 and =2.2.0) include fixes for:

  • RUSTSEC-2022-0093 (ed25519-dalek Double Public Key Signing Function Oracle Attack, fixed in 2.0.0 by the SigningKey / VerifyingKey API redesign).
  • RUSTSEC-2024-0344 (curve25519-dalek Scalar29::sub / Scalar52::sub LLVM-inserted timing leak, fixed in 4.1.3).

WebBuf pins these crates exactly. Cargo will not silently upgrade across RUSTSEC-fix points without an intentional bump in WebBuf's Cargo.toml.

See issues/0007-curve25519-hybrid-pq for the full crate-survey rationale and pinned-version decisions.

Tests

  • 13 Rust tests: four RFC 8032 §7.1 KATs (TEST 1 empty, TEST 2 1-byte, TEST 3 2-byte, TEST SHA(abc)), determinism, round-trip, tampered-message rejection, tampered-signature rejection (R and S separately), wrong-public-key rejection, malformed-public-key graceful rejection, all-zero-signature rejection, input-length error wording, and small-order-public-key universal-forgery rejection (asserts that the identity-element pub key + identity-R / zero-S signature is rejected for any message — this is what verify_strict buys us over the cofactored verify).
  • 25 TypeScript tests: round-trip on random keys, length invariants, deterministic public-key + signature derivation, empty-message + 64 KiB message round-trip, all seven rejection paths (including the matching universal-forgery regression), plus the four RFC 8032 §7.1 audit KATs each asserting public-key derivation, signature production, and verification.
pnpm test

License

MIT