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.
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
Types 1
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.
private ulong[16] limbs_void toBytes(out ubyte[32] outBytes) @trusted nothrow @nogcWrites the canonical 32-byte little-endian encoding.Fe25519 fromBytes(scope const(ubyte)[32] bytes) static pure nothrow @safe @nogcLoads a field element from 32 little-endian bytes.Fe25519 addRef(scope const ref Fe25519 other) pure nothrow @safe @nogc constReturns this + other mod p without copying other.Fe25519 subRef(scope const ref Fe25519 other) pure nothrow @safe @nogc constReturns this - other mod p without copying other.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.void cswap(uint doSwap, ref Fe25519 other) pure nothrow @safe @nogcConditionally swaps this element with another.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.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
ubyte hexNibble(char c) pure @safe nothrow @nogcConverts one hexadecimal digit to its value.ubyte[32] hexToBytes(string hex) pure @safe nothrow @nogcDecodes exactly 64 hexadecimal digits into 32 bytes.bool expectBytes(Fe25519 fe, string expectedHex) @safe nothrow @nogcCompares a field element against a 64-digit hex expectation.