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.subtleis unavailable ([crypto] NOT READYlogged)- A key guard fails:
key.algorithm.name !== 'Ed25519'([crypto] INVALIDlogged) - An invalid
formatis supplied ([crypto] INVALIDlogged) - An invalid input type is supplied (
[crypto] INVALIDlogged) crypto.subtleitself rejects ([crypto] FAILlogged)
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 enginesgenerateKey(and all ops) resolvefalse; no feature-detect beyond thecrypto.subtleguard is performed. Probe withawait generateKey()at startup if runtime availability is uncertain. - No-throw contract: all methods are
asyncand resolve to a result orfalse.crypto.subtlerejections are caught and logged; they never propagate. - Key guards:
sign,verify, andexportKeycheckkey.algorithm.name === 'Ed25519'before calling subtle, resolvingfalseif the guard fails. - Worker-safe:
crypto.subtleis available in Web Workers; this module has no DOM dependency and no main-thread closures.