ed25519: Pure-Racket Ed25519 Signatures
| (require ed25519) | package: ed25519 |
This package implements Ed25519 signatures exactly as specified in RFC 8032 using only pure Racket: no FFI, no shared libraries, no external tools. It exists as the last-resort fallback of a provider chain: if no native provider (such as OpenSSL 3 via crypto-lib, libsodium, or Nettle) is available, Ed25519 signing and verification still work in any Racket environment.
Signatures are deterministic: the same message and secret seed always produce the same 64-byte signature.
The implementation matches RFC 8032 and is validated against all RFC 8032 section 7.1 test vectors plus byte-for-byte cross-validation against OpenSSL 3.
It is not constant time. Verification of public data is safe anywhere; production key holders should prefer a native provider (see Security Considerations).
1 Quick Start
> "(require ed25519)" "(require ed25519)"
> "\n" "\n"
> "\n" "\n"
> "(define kp (generate-keypair))" "(define kp (generate-keypair))"
> "\n" "\n"
> "(define message #\"update manifest 2026-11-04\")" "(define message #\"update manifest 2026-11-04\")"
> "\n" "\n"
> "(define sig (sign message (keypair-secret kp)))" "(define sig (sign message (keypair-secret kp)))"
> "\n" "\n"
> "(verify message sig (keypair-public kp))" "(verify message sig (keypair-public kp))"
A key pair carries the public key and the secret seed, both as 32-byte byte strings. The secret seed is the RFC 8032 seed: 32 bytes of entropy, not the 64-byte expanded form.
> "(require ed25519)" "(require ed25519)"
> "\n" "\n"
> "\n" "\n"
> "(define seed (crypto-random-bytes 32)) ; caller-provided seed" "(define seed (crypto-random-bytes 32)) ; caller-provided seed"
> "\n" "\n"
> "(secret->public seed) ; derive the public key" "(secret->public seed) ; derive the public key"
2 Keys
struct
(struct ed25519-keypair (public secret))
public : bytes? secret : bytes?
procedure
procedure
(keypair-public kp) → bytes?
kp : ed25519-keypair?
procedure
(keypair-secret kp) → bytes?
kp : ed25519-keypair?
procedure
(secret->public secret) → bytes?
secret : bytes?
3 Signing and Verification
All cryptographic failures return #f and never raise: tampered messages or signatures, wrong keys, non-canonical point encodings, non-canonical scalars (S at or above the group order), and small-order public keys are all rejected. Length or type violations raise exn:fail:contract.
4 Constants
value
public-key-length : 32
value
secret-key-length : 32
value
signature-length : 64
5 Security Considerations
Not constant time. Racket bignum arithmetic leaks timing information. Verification of public data (update manifests, license proofs, signed attestations) is the intended use. Production issuers should sign with a native provider and use this implementation only as a fallback in controlled environments.
No third-party audit. The implementation follows RFC 8032 and ref10’s decompression technique, is validated against all RFC 8032 vectors, and is cross-checked byte-for-byte against OpenSSL 3, but it has not undergone an independent cryptographic audit.
This package signs, it does not encrypt. Ed25519 is a signature scheme; it provides no confidentiality and no key agreement.
Secrets are ordinary byte strings. Transparent structures can print secret seeds; keep them out of logs and error messages.
Provenance. The algorithm follows RFC 8032 section 5.1 directly, with the decompression technique from the public-domain ref10 reference implementation (D. J. Bernstein et al.) and SHA-512 per FIPS 180-4. See the package NOTICE file for full attribution.