WebCrypto-backed Ed25519 (RFC 8032) sign/verify via crypto.subtle.

Module webcryptoEd25519 | Source packages/front/fw/src/crypto/webcrypto/ed25519.js | Deps none | Worker-safe yes

Opt-in alternative to the pure-JS ed25519 module. Async, Uint8Array/CryptoKey/CryptoKeyPair. Every method resolves to a result or false — never rejects. Supports generate, sign, verify, and import/export in raw, spki, pkcs8, and jwk formats.

Resolve

const webcryptoEd25519 = runtime.resolve('webcryptoEd25519');
// Returns: { isAvailable, generateKey, sign, verify, importKey, exportKey }

API

Method Signature Returns
isAvailable () => boolean true when crypto.subtle is present
generateKey (extractable?: boolean) => Promise<CryptoKeyPair|false> Key pair or false if Ed25519 is unsupported
sign (privateKey: CryptoKey, message: Uint8Array) => Promise<Uint8Array|false> 64-byte signature or false on error
verify (publicKey: CryptoKey, signature: Uint8Array, message: Uint8Array) => Promise<boolean> true if valid, false otherwise
importKey (format: 'raw'|'spki'|'pkcs8'|'jwk', keyData: Uint8Array|object, usages: KeyUsage[], extractable?: boolean) => Promise<CryptoKey|false> Imported CryptoKey or false
exportKey (format: 'raw'|'spki'|'pkcs8'|'jwk', key: CryptoKey) => Promise<Uint8Array|object|false> Raw/DER → Uint8Array; jwk → object; false on error

generateKey defaults extractable to true. importKey defaults extractable to false for pkcs8 (private key material) and true for all other formats.

Every operation resolves false when:

  • crypto.subtle is unavailable ([crypto] NOT READY logged)
  • A key guard fails: key.algorithm.name !== 'Ed25519' ([crypto] INVALID logged)
  • An invalid format is supplied ([crypto] INVALID logged)
  • An invalid input type is supplied ([crypto] INVALID logged)
  • crypto.subtle itself rejects ([crypto] FAIL logged)

Examples

Generate and sign

const kp = await webcryptoEd25519.generateKey();
if (kp === false) {
    // Ed25519 unsupported on this engine — fall back to pure-JS ed25519
}

const message = new TextEncoder().encode('hello');
const signature = await webcryptoEd25519.sign(kp.privateKey, message);
// signature is a Uint8Array of 64 bytes

Verify

const valid = await webcryptoEd25519.verify(kp.publicKey, signature, message);
// valid === true

Import a raw public key (32 bytes)

// pubKeyBytes: Uint8Array of 32 bytes (raw Ed25519 public key)
const pubKey = await webcryptoEd25519.importKey('raw', pubKeyBytes, ['verify']);
const ok = await webcryptoEd25519.verify(pubKey, signature, message);

Export / import round-trip

const rawBytes = await webcryptoEd25519.exportKey('raw', kp.publicKey);
// rawBytes is a 32-byte Uint8Array

const jwk = await webcryptoEd25519.exportKey('jwk', kp.publicKey);
// jwk is { kty: 'OKP', crv: 'Ed25519', x: '...', ... }

Availability guard

if (!webcryptoEd25519.isAvailable()) {
    // crypto.subtle is absent — use pure-JS ed25519 module instead
}

Worker Usage

const worker = fw.createWorker(
    function ({ libs, args }) {
        const enc = (s) => new TextEncoder().encode(s);
        libs.webcryptoEd25519.generateKey().then(async (kp) => {
            if (!kp) { self.postMessage(null); return; }
            const sig = await libs.webcryptoEd25519.sign(kp.privateKey, enc(args[0]));
            self.postMessage(sig);
        });
    },
    { dependencies: ['webcryptoEd25519'], args: ['hello'] }
);

Notes

  • RFC 8032: Ed25519 is specified in RFC 8032. Signatures are deterministic (no random nonce) and always 64 bytes. Public keys are 32 bytes in raw format.
  • Engine availability: 'Ed25519' is a newer WebCrypto algorithm — Chrome ≥ 113, Firefox ≥ 130, Safari ≥ 17. On older engines generateKey (and all ops) resolve false; no feature-detect beyond the crypto.subtle guard is performed. Probe with await generateKey() at startup if runtime availability is uncertain.
  • No-throw contract: all methods are async and resolve to a result or false. crypto.subtle rejections are caught and logged; they never propagate.
  • Key guards: sign, verify, and exportKey check key.algorithm.name === 'Ed25519' before calling subtle, resolving false if the guard fails.
  • Worker-safe: crypto.subtle is available in Web Workers; this module has no DOM dependency and no main-thread closures.

See also

  • ed25519 — pure-JS Ed25519 (synchronous, bitArray I/O, no WebCrypto)
  • x25519 — WebCrypto X25519 key agreement (ECDH over Curve25519)
  • ecc — WebCrypto ECDSA/ECDH over NIST curves