Skip to content

Instantly share code, notes, and snippets.

@GuiBibeau
Created August 12, 2025 17:52
Show Gist options
  • Save GuiBibeau/680447dbac9193876718e20cda8f6793 to your computer and use it in GitHub Desktop.
Save GuiBibeau/680447dbac9193876718e20cda8f6793 to your computer and use it in GitHub Desktop.
vanilla typescript implementation to check if an address is on the Ed25519 elliptic curve
async function isOnCurve(bytes: Uint8Array): Promise<boolean> {
if (bytes.length \!== 32) {
return false;
}
// Constants
const p = 2n ** 255n - 19n;
const d = mod(-121665n * modInverse(121666n, p), p);
// Decode y-coordinate from bytes (little-endian)
let y = 0n;
for (let i = 0; i < 32; i++) {
y += BigInt(bytes[i] ?? 0) << BigInt(8 * i);
}
// Extract sign bit (bit 255)
const sign = (y >> 255n) & 1n;
// Clear the sign bit for y
y &= (1n << 255n) - 1n;
// Check y is in valid range [0, p-1]
if (y >= p) {
return false;
}
// Compute y² mod p
const y2 = modMul(y, y, p);
// Compute u = y² - 1 mod p
const u = modSub(y2, 1n, p);
// Compute v = d·y² + 1 mod p
const v = modAdd(modMul(d, y2, p), 1n, p);
// Compute inv(v) mod p
const invV = modInverse(v, p);
// If v is not invertible (v == 0 mod p), invalid
if (invV === 0n) {
return false;
}
// Compute x² = u · inv(v) mod p
const x2 = modMul(u, invV, p);
// Compute sqrt(x²) mod p
const x = modSqrt(x2, p);
// If no square root exists, not on curve
if (x === null) {
return false;
}
// Special case: if x == 0 and sign == 1, invalid
if (x === 0n && sign === 1n) {
return false;
}
// Otherwise, it is on the curve
return true;
}
// Helper functions
function mod(a: bigint, p: bigint): bigint {
const res = a % p;
return res < 0n ? res + p : res;
}
function modAdd(a: bigint, b: bigint, p: bigint): bigint {
return mod(a + b, p);
}
function modSub(a: bigint, b: bigint, p: bigint): bigint {
return mod(a - b, p);
}
function modMul(a: bigint, b: bigint, p: bigint): bigint {
return mod(a * b, p);
}
function modPow(base: bigint, exp: bigint, p: bigint): bigint {
let res = 1n;
let b = mod(base, p);
let e = exp;
while (e > 0n) {
if (e & 1n) {
res = modMul(res, b, p);
}
b = modMul(b, b, p);
e >>= 1n;
}
return res;
}
function modInverse(a: bigint, p: bigint): bigint {
return modPow(a, p - 2n, p);
}
function modSqrt(a: bigint, p: bigint): bigint | null {
const amod = mod(a, p);
if (amod === 0n) {
return 0n;
}
const legendre = modPow(amod, (p - 1n) / 2n, p);
if (legendre \!== 1n) {
return null;
}
const i = modPow(2n, (p - 1n) / 4n, p); // sqrt(-1) mod p
let r = modPow(amod, (p + 3n) / 8n, p);
if (mod(r * r, p) === amod) {
return r;
}
r = mod(r * i, p);
if (mod(r * r, p) === amod) {
return r;
}
return null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment