X25519 (RFC 7748) — Diffie-Hellman over Curve25519, primitive ECDH minimaliste.

Module x25519 | Source packages/front/fw/src/crypto/pkc/x25519.js | Deps none | Worker-safe yes

Outside NIST but a de facto standard (TLS 1.3, Signal, WireGuard). TweetNaCl-derived implementation (public domain; see provenance). isLowOrderPoint helper added for explicit rejection before scalarMult.

Resolve

const x25519 = runtime.resolve('x25519');
// Returns: { scalarMult, scalarMultBase, isLowOrderPoint, _internal }

API

Method Signature Returns
scalarMult(scalar, u) (Uint8Array(32), Uint8Array(32)) => Uint8Array(32) | false Shared secret (clamping auto per RFC 7748 §5)
scalarMultBase(scalar) (Uint8Array(32)) => Uint8Array(32) scalarMult(scalar, basePoint=9) (public key)
isLowOrderPoint(u) (Uint8Array(32)) => boolean true if u is low-order (cofactor 8)
_internal.lowOrderPoints Uint8Array[7] Table of the 7 documented LOPs

Examples

Diffie-Hellman

const { x25519, random } = fw.runtime.resolveAll(['x25519', 'random']);

const aPriv = random.bytes(32);
const aPub  = x25519.scalarMultBase(aPriv);

const bPriv = random.bytes(32);
const bPub  = x25519.scalarMultBase(bPriv);

// Exchange: Alice sends aPub, receives bPub
const sharedA = x25519.scalarMult(aPriv, bPub);
const sharedB = x25519.scalarMult(bPriv, aPub);
// sharedA === sharedB

Low-order point rejection (before scalarMult)

if (x25519.isLowOrderPoint(receivedPub)) {
    console.warn('Reject: low-order public key');
    return;
}
const shared = x25519.scalarMult(myPriv, receivedPub);

Worker Usage

const worker = fw.createWorker(
    function ({ libs, args }) {
        self.postMessage(libs.x25519.scalarMult(args[0], args[1]));
    },
    { dependencies: ['x25519'], args: [scalar, u] }
);

Notes

  • Automatic clamping per RFC 7748 §5 (clear low 3 bits, clear high bit, set bit 254).
  • Low-order points: scalarMult(scalar, lowOrderU) === 0...0. RFC 7748 §6.1 allows this (non-rejected by design); isLowOrderPoint enables explicit caller-side rejection (constant-time vs 7 documented LOPs).
  • scalarMult not formally constant-time: _M/_S Float64 + carry. Acceptable per RFC 7748 §A.1 in an interactive ECDH context.
  • RFC 7748 1M iterations validation gated on CRYPTO_FULL=1.

See also