ddn.crypto.internal.fe25519

Fixed-width field arithmetic for GF(2^255 - 19).

Field elements are held as sixteen little-endian 16-bit limbs in ulong slots. All operations in this module have control flow independent of the secret values: fixed carry chains, mask-based conditional swaps and subtractions, no heap allocation, and no lookups indexed by secret data. The representation is slightly redundant between operations (limbs stay below 2^17); toBytes produces the canonical encoding.

Bound analysis: with input limbs below 2^17, a multiplication column accumulates at most 16 2^34 + 38 15 * 2^34 + carry < 2^41, far below the ulong limit, so the schoolbook carry chains need no data-dependent loops, and a wrapped carry folds back as 2^256 mod p = 38.

Security

Note

passing known-answer tests is not proof of

constant-time behaviour; this code keeps branches and memory access independent of secrets by construction, but compilers and CPUs may still introduce timing variations. See ddn.crypto.ct for the general side-channel guidance.

Types 1

structFe25519

An element of GF(2^255 - 19) in loose 16x16-bit limb form.

The represented value is sum(limbs[i] << (16 * i)) modulo p = 2^255 - 19; after every completing operation each limb is below 2^17 and the represented integer is below 2^256 + 38.

Fields
private ulong[16] limbs_
Methods
void wipe() @trusted nothrow @nogcZeroizes all limbs.
void toBytes(out ubyte[32] outBytes) @trusted nothrow @nogcWrites the canonical 32-byte little-endian encoding.
Fe25519 zero() static pure nothrow @safe @nogcReturns: the additive identity.
Fe25519 one() static pure nothrow @safe @nogcReturns: the multiplicative identity.
Fe25519 fromBytes(scope const(ubyte)[32] bytes) static pure nothrow @safe @nogcLoads a field element from 32 little-endian bytes.
Fe25519 add(Fe25519 other) pure nothrow @safe @nogc constReturns: this + other mod p.
Fe25519 addRef(scope const ref Fe25519 other) pure nothrow @safe @nogc constReturns this + other mod p without copying other.
Fe25519 sub(Fe25519 other) pure nothrow @safe @nogc constReturns: this - other mod p.
Fe25519 subRef(scope const ref Fe25519 other) pure nothrow @safe @nogc constReturns this - other mod p without copying other.
Fe25519 mul(Fe25519 other) pure nothrow @safe @nogc constReturns: this * other mod p.
Fe25519 mulRef(scope const ref Fe25519 other) pure nothrow @safe @nogc constReturns this * other mod p without copying other.
Fe25519 mulSmall(ulong m) pure nothrow @safe @nogc constReturns: this * m mod p for a small public multiplier.
Fe25519 sq() pure nothrow @safe @nogc constReturns: this * this mod p.
Fe25519 invert() pure nothrow @safe @nogc constReturns: this^(p-2 mod p), the field inverse.
void cswap(uint doSwap, ref Fe25519 other) pure nothrow @safe @nogcConditionally swaps this element with another.
private void normalizeTail() pure nothrow @safe @nogcPropagates the small overflow left in limb zero after a wrapped carry. Fixed two-step chain; the bound analysis in the module documentation guarantees at most a couple of units reach step 2.
private void canonicalize() pure nothrow @safe @nogcReduces to the canonical range [0) so toBytes emits minimal encodings: folds the weight at 2^255 (factor 19), then conditionally subtracts p exactly once. All steps are mask-based.

Functions 4

private fnubyte hexNibble(char c) pure @safe nothrow @nogcConverts one hexadecimal digit to its value.
private fnubyte[32] hexToBytes(string hex) pure @safe nothrow @nogcDecodes exactly 64 hexadecimal digits into 32 bytes.
private fnstring hexString(Fe25519 fe) @safeFormats a field element as 64 lowercase hex digits.
private fnbool expectBytes(Fe25519 fe, string expectedHex) @safe nothrow @nogcCompares a field element against a 64-digit hex expectation.