rust-ed25519-compact

A small, self-contained, wasm-friendly Ed25519 and X25519 implementation for Rust with zero mandatory dependencies.

Library
Cargo
v2.4.2
145stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
66/100Good
Development Activity68
Maintenance48
Community68
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture85
Code Quality78
Innovation62
Learning Curve80

ed25519-compact is a Rust crate implementing Ed25519 digital signatures and X25519 key exchange from first principles, with formally-verified Curve25519 field arithmetic at its core. It targets environments where most cryptography crates struggle: no_std embedded systems, WebAssembly modules, and edge compute platforms like Fastly Compute, while remaining a drop-in choice for ordinary server and CLI applications.

The crate has effectively zero dependencies when the calling application supplies its own randomness, and pulls in only getrandom otherwise. Beyond the standard one-shot sign/verify API, it supports incremental (streaming) signing and verification so large messages can be processed in chunks without buffering the whole payload in memory, plus optional PEM import/export, key blinding, and a size-optimized build mode for constrained targets.

Maintained by Frank Denis (the author of libsodium and dnscrypt-proxy), the project has a strong pedigree in applied cryptography and prioritizes correctness and portability over feature breadth, making it a common choice when a project needs Ed25519 without pulling in a large crypto stack.

What You Get

  • One-shot sign()/verify() API for Ed25519 signatures over any AsRef<[u8]> message
  • Incremental streaming API (SigningState/VerifyingState) for signing and verifying large messages in chunks without buffering them
  • X25519 key exchange support behind the x25519 feature flag
  • PEM import/export for OpenSSL-compatible key files behind the pem feature flag
  • Key blinding support behind the blind-keys feature flag
  • A disable-signatures build mode that compiles only the X25519 code path for minimal binary size

Common Use Cases

  • Signing and verifying authentication tokens or API requests in a no_std embedded firmware target
  • Implementing Ed25519 signature verification inside a WebAssembly module or Fastly Compute@Edge service
  • Streaming-signing large files or payloads without loading the entire message into memory
  • Building a minimal-dependency Rust CLI or library that needs Ed25519 without pulling in a full crypto framework
  • Performing X25519 Diffie-Hellman key exchange alongside Ed25519 signing in the same crate

Under The Hood

Architecture The crate is organized as a small set of focused modules under src/: field25519.rs implements the underlying Curve25519 field arithmetic, edwards25519.rs builds Edwards-curve point operations on top of it, ed25519.rs exposes the public PublicKey/SecretKey/Signature/KeyPair types and the one-shot and incremental sign/verify APIs, x25519.rs provides the separate X25519 key-exchange path, and sha512.rs/common.rs/error.rs supply shared hashing, helper types, and a single Error enum used across the crate. lib.rs wires these together behind Cargo feature flags (disable-signatures, x25519, pem, std) using #[cfg] gating, so unused code paths are compiled out entirely rather than dead-code-eliminated at link time — a deliberate design for no_std and size-constrained targets. Sensitive types like SecretKey and the incremental signing/verifying state implement Drop to zero memory on scope exit, and the module boundaries map directly onto the mathematical layers (field arithmetic -> curve arithmetic -> signature scheme), so a change to the field implementation is isolated from the public signing API.

Tech Stack The crate targets Rust 2018 edition and is no_std-compatible by default, opting into the standard library only behind the std feature. Its dependency footprint is deliberately minimal: getrandom (optional, gated by random) supplies secure randomness including a wasm_js backend for WebAssembly targets, ct-codecs (optional, gated by pem) provides constant-time base64/PEM codecs, and ed25519 (optional, gated by traits) adds interop with the broader ed25519/signature crate ecosystem. There is no build script or code generation step; the field and curve arithmetic are hand-written Rust rather than generated from a spec. A .cargo/config.toml sets getrandom_backend="wasm_js" for wasm32-unknown-unknown builds, reflecting the crate’s explicit WebAssembly and Fastly Compute@Edge deployment targets.

Code Quality Tests are embedded as #[test] functions directly inside the relevant modules (ed25519.rs, x25519.rs, pem.rs) rather than in a separate tests/ directory, exercising sign/verify round-trips, incremental signing, and PEM parsing. CI (.github/workflows/rust.yml) runs cargo test with default features, a no_std build with --no-default-features, a build with the full optional feature set (pem,traits,self-verify,blind-keys,opt_size), and a disable-signatures-only build, so the feature matrix is exercised on every push rather than assumed to compile. Error handling is explicit throughout: a single Error enum with Display/std::error::Error impls (the latter gated behind std) is returned via Result from all fallible operations instead of panicking. lib.rs carries a short, deliberate list of #[allow(clippy::...)] lints for patterns intrinsic to low-level field arithmetic (single-letter variable names, literal casts), indicating clippy is run as part of the development workflow even though it isn’t wired into CI directly.

API Design The public API favors small, purpose-built newtypes (PublicKey, SecretKey, Signature, Seed, Noise) that Deref to byte slices, so callers can treat them as raw bytes when needed while still getting type-level guarantees elsewhere. The one-shot path (KeyPair::generate(), sk.sign(), pk.verify()) requires only a few lines to get started, matching the crate’s own README example, and the incremental API mirrors it closely (sign_incremental()/verify_incremental() plus repeated absorb() calls) so streaming usage doesn’t require learning a separate mental model. Optional capabilities (X25519, PEM, blinding, trait interop) are opt-in via Cargo features rather than always-present surface area, which keeps the default API small at the cost of requiring users to know which feature flag unlocks a given method — a reasonable tradeoff for a crate explicitly optimizing for constrained and no_std targets.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search