cheatah
Module

ec

Functions

fn bool is_zero(const fe< C > &a) source#

Whether every limb of a is zero (an OR-accumulate over all limbs, no early exit).

Template parameters
C

the curve traits.

Parameters
a

the value to test.

Returns

true iff a == 0.

Complexity

O(1) — kLimbs limb reads on a fixed-width value.

Allocation

none.

fn bool geq(const fe< C > &a, const fe< C > &b) source#

Multi-limb unsigned compare, most-significant limb first.

Template parameters
C

the curve traits.

Parameters
a

left operand.

b

right operand.

Returns

true iff a >= b.

Complexity

O(1) — at most kLimbs limb compares.

Allocation

none.

fn u64 sub_borrow(fe< C > &r, const fe< C > &a, const fe< C > &b) source#

r = a - b (mod 2^kBits), returns the borrow.

Template parameters
C

the curve traits.

Parameters
r

receives the difference.

a

minuend.

b

subtrahend.

Returns

the final borrow: 1 iff a < b, else 0.

Complexity

O(1) — one pass over kLimbs limbs.

Allocation

none.

fn u64 add_carry(fe< C > &r, const fe< C > &a, const fe< C > &b) source#

r = a + b (mod 2^kBits), returns the carry.

Template parameters
C

the curve traits.

Parameters
r

receives the sum.

a

first addend.

b

second addend.

Returns

the final carry out of the top limb (0 or 1).

Complexity

O(1) — one pass over kLimbs limbs.

Allocation

none.

fn void mont_mul(fe< C > &r, const fe< C > &a, const fe< C > &b, const Mont< C > &M) source#

CIOS Montgomery multiplication: r = a*b*R^-1 mod m.

Template parameters
C

the curve traits.

Parameters
r

receives the product.

a

first factor (Montgomery form).

b

second factor (Montgomery form).

M

the Montgomery context.

Complexity

O(1) — kLimbs^2 limb multiplies on a fixed-width value.

Allocation

none.

fn void mont_add(fe< C > &r, const fe< C > &a, const fe< C > &b, const Mont< C > &M) source#

Modular addition: r = a + b mod m (add, then one conditional subtract of m).

Template parameters
C

the curve traits.

Parameters
r

receives the sum.

a

first addend.

b

second addend.

M

the Montgomery context (only its modulus is used).

Complexity

O(1).

Allocation

none.

fn void mont_sub(fe< C > &r, const fe< C > &a, const fe< C > &b, const Mont< C > &M) source#

Modular subtraction: r = a - b mod m (subtract, then one conditional add of m on borrow).

Template parameters
C

the curve traits.

Parameters
r

receives the difference.

a

minuend.

b

subtrahend.

M

the Montgomery context (only its modulus is used).

Complexity

O(1).

Allocation

none.

fn void to_mont(fe< C > &r, const fe< C > &a, const Mont< C > &M) source#

Convert a into Montgomery form: r = a*R mod m (one mont_mul by R^2).

Template parameters
C

the curve traits.

Parameters
r

receives the Montgomery form.

a

the plain value.

M

the Montgomery context.

Complexity

O(1) — one mont_mul.

Allocation

none.

fn void from_mont(fe< C > &r, const fe< C > &a, const Mont< C > &M) source#

Convert a out of Montgomery form: r = a*R^-1 mod m (one mont_mul by 1).

Template parameters
C

the curve traits.

Parameters
r

receives the plain value.

a

the Montgomery-form value.

M

the Montgomery context.

Complexity

O(1) — one mont_mul.

Allocation

none.

fn void mont_inv(fe< C > &r, const fe< C > &a, const Mont< C > &M) source#

r = a^-1 mod m, via Fermat: a^(m-2).

(m is prime for both p and n.)

Template parameters
C

the curve traits.

Parameters
r

receives the inverse (Montgomery form).

a

the value to invert (Montgomery form, nonzero).

M

the Montgomery context.

Complexity

O(1) — a fixed kBits-step square-and-multiply ladder.

Allocation

none.

fn u64 inv64(u64 a) source#

a^-1 mod 2^64 (a odd), by Newton's iteration.

Parameters
a

the odd value to invert.

Returns

the inverse mod 2^64.

Complexity

O(1) — five fixed Newton steps.

Allocation

none.

fn Mont< C > make_mont(const fe< C > &m) source#

Build the Montgomery context for modulus m — every constant (n0, R^2 mod m, R mod m) derived at startup, no hand-transcribed Montgomery magic.

Template parameters
C

the curve traits.

Parameters
m

the (odd, prime) modulus.

Returns

the derived context.

Complexity

O(1) — 2*kBits fixed doubling steps to derive R^2 mod m.

Allocation

none.

fn const Mont< C > & Fp() source#

The curve's field context: the Montgomery context for the prime P, built once per instantiation (function-local static).

Template parameters
C

the curve traits.

Returns

the context mod C::P.

Complexity

O(1) after the one-time static make_mont on first use.

Allocation

none — static storage.

fn const Mont< C > & Fn() source#

The curve's scalar context: the Montgomery context for the group order N, built once per instantiation (function-local static).

Template parameters
C

the curve traits.

Returns

the context mod C::N.

Complexity

O(1) after the one-time static make_mont on first use.

Allocation

none — static storage.

fn fe< C > be_to_fe(const unsigned char *b) source#

Load kBytes big-endian bytes into a little-endian limb array.

Template parameters
C

the curve traits.

Parameters
b

pointer to kBytes bytes, most significant first.

Returns

the value.

Complexity

O(1) — kBytes byte reads.

Allocation

none.

fn void fe_to_be(unsigned char *out, const fe< C > &a) source#

Store a limb array as kBytes big-endian bytes (the inverse of be_to_fe).

Template parameters
C

the curve traits.

Parameters
out

receives kBytes bytes, most significant first.

a

the value to serialize.

Complexity

O(1) — kBytes byte writes.

Allocation

none.

fn Jac< C > jac_infinity() source#

The point at infinity (the group identity): Z = 0 with the explicit flag set.

Template parameters
C

the curve traits.

Returns

the identity point.

Complexity

O(1).

Allocation

none.

fn void jac_double(Jac< C > &r, const Jac< C > &q) source#

Jacobian point doubling, r = 2q, using the a = -3 formulas (true of every NIST prime curve).

Branchy (early-returns on infinity): for PUBLIC data only — the secret path uses jac_double_ct.

Template parameters
C

the curve traits.

Parameters
r

receives the doubled point.

q

the point to double.

Complexity

O(1) — a fixed count of field operations.

Allocation

none.

fn void jac_add(Jac< C > &r, const Jac< C > &a, const Jac< C > &b) source#

Jacobian point addition, r = a + b, with branchy special cases (either operand infinity, a == b -> double, a == -b -> infinity).

For PUBLIC data only — the secret path uses jac_add_ct.

Template parameters
C

the curve traits.

Parameters
r

receives the sum.

a

first point.

b

second point.

Complexity

O(1) — a fixed count of field operations.

Allocation

none.

fn void jac_double_mul(Jac< C > &r, const fe< C > &u1, const Jac< C > &A, const fe< C > &u2, const Jac< C > &B) source#

Strauss-Shamir: u1*A + u2*B with ONE doubling chain (kBits doublings total) instead of two separate scalar multiplications.

A 2-bit window over both scalars uses a 16-entry combined table [i*A + j*B] so it also halves the adds.

Template parameters
C

the curve traits.

Parameters
r

receives u1*A + u2*B.

u1

first (public) scalar.

A

first point.

u2

second (public) scalar.

B

second point.

Complexity

O(1) — kBits doublings plus at most kBits/2 adds.

Allocation

none — the 16-entry window table lives on the stack.

fn fe< C > jac_affine_x(const Jac< C > &q) source#

The affine x-coordinate (normal form) of a Jacobian point: x = X / Z^2.

Template parameters
C

the curve traits.

Parameters
q

the point (not infinity: Z must be invertible).

Returns

x out of Montgomery form.

Complexity

O(1) — dominated by one mont_inv (a fixed Fermat ladder).

Allocation

none.

fn Jac< C > affine_to_jac(const fe< C > &x, const fe< C > &y) source#

Lift an affine point into Jacobian Montgomery form (Z = 1).

Template parameters
C

the curve traits.

Parameters
x

the affine x-coordinate (plain form).

y

the affine y-coordinate (plain form).

Returns

the Jacobian point.

Complexity

O(1) — two to_mont conversions.

Allocation

none.

fn const Jac< C > & base_point() source#

The curve base point G, lifted to Jacobian form once (function-local static).

Template parameters
C

the curve traits.

Returns

G.

Complexity

O(1) after the one-time static lift on first use.

Allocation

none — static storage.

fn const std::array< Jac< C >,(1u<< C::kLimbs)> & g_comb() source#

Fixed-base comb for k*G.

G is constant, so we precompute (once) the 2^kLimbs-entry table T[s] = sum over set bits i of s of (2^(64*i) * G). Then k*G is just 64 doublings + 64 adds (vs kBits doublings for a generic window) — the big win for the per-message signing path. Selector at step j is bit j of each 64-bit limb.

Template parameters
C

the curve traits.

Returns

the comb table.

Complexity

O(1) after the one-time static build (kLimbs*64 doublings plus the subset sums).

Allocation

none — the table is a function-local static std::array.

fn u64 ct_mask(bool c) source#

Branch-free boolean-to-mask: false -> 0, true -> all-ones.

Parameters
c

the condition.

Returns

the 64-bit mask.

Complexity

O(1).

Allocation

none.

fn void fe_cmov(fe< C > &r, const fe< C > &a, u64 m) source#

Constant-time conditional move over a field element: r = m ?

a : r, per limb, no branch.

Template parameters
C

the curve traits.

Parameters
r

the destination (kept when m is 0).

a

the source (copied when m is all-ones).

m

the ct_mask (0 or all-ones).

Complexity

O(1).

Allocation

none.

fn void jac_cmov(Jac< C > &r, const Jac< C > &a, u64 m) source#

Constant-time conditional move over a Jacobian point (all three coordinates via fe_cmov; the inf flag is recomputed from Z, which encodes infinity throughout the CT path).

Template parameters
C

the curve traits.

Parameters
r

the destination point.

a

the source point.

m

the ct_mask (0 or all-ones).

Complexity

O(1).

Allocation

none.

fn void jac_double_ct(Jac< C > &r, const Jac< C > &q) source#

Point doubling WITHOUT the is-infinity early return: the formula's Z3 = 2*Y*Z is already 0 when the input is infinity (Z==0), so it self-encodes infinity, and a prime-order curve has no finite 2-torsion point that could double TO infinity — so no branch is needed.

Template parameters
C

the curve traits.

Parameters
r

receives 2q.

q

the point to double.

Complexity

O(1) — the same fixed field-operation count for every input.

Allocation

none.

fn void jac_add_ct(Jac< C > &r, const Jac< C > &a, const Jac< C > &b) source#

Point addition, branch-free.

It always computes the general add formula, then constant-time- selects the correct result over the special cases via masks: a==inf -> b, b==inf -> a, a==b -> double(a), a==-b -> infinity. Precedence is enforced by cmov ORDER (a==inf last / highest).

Template parameters
C

the curve traits.

Parameters
r

receives a + b.

a

first point.

b

second point.

Complexity

O(1) — the same fixed field-operation count for every input (the double is always computed).

Allocation

none.

fn void ct_select(Jac< C > &out, const std::array< Jac< C >, N > &tbl, unsigned sel) source#

Constant-time table lookup: scan every entry, copying the one whose index == sel via a mask, so the memory-access pattern (and timing) is independent of the secret selector.

Template parameters
C

the curve traits.

N

the table size.

Parameters
out

receives tbl[sel].

tbl

the table.

sel

the (secret) index.

Complexity

O(N) — every entry is scanned by design.

Allocation

none.

fn void jac_mul_base(Jac< C > &r, const fe< C > &k) source#

k*G for a SECRET scalar k, in constant time: 64 doublings + 64 unconditional adds over the fixed-base comb table.

The old form skipped the add when the window was zero and indexed the table by the secret selector — both leaked bits of k. Here every step does the same work (branch-free double, masked table select, unconditional branch-free add — add of the T[0]=infinity entry when the window is zero is a no-op via the CT add's masks).

Template parameters
C

the curve traits.

Parameters
r

receives k*G.

k

the secret scalar.

Complexity

O(1) — exactly 64 CT doublings, 64 CT table scans, and 64 CT adds.

Allocation

none.

fn bool ct_add_selfcheck() source#

Differential self-check for the constant-time point ops.

A TEMPLATE, instantiated ONLY by the p256/p384 test seam (so there is no such code in a production build), it confirms jac_add_ct / jac_double_ct agree with the branchy reference jac_add / jac_double on the general case AND every special case — a==b, a==-b, and infinity operands — which the signing path exercises rarely or never, so this both proves correctness and drives those branches for coverage.

Template parameters
C

the curve traits.

Returns

true iff every CT result matches the branchy reference.

Complexity

O(1) — a fixed handful of point operations.

Allocation

none.

fn fe< C > reduce_mod_n(const fe< C > &v) source#

Reduce a scalar already known to be < 2n into [0, n): a single conditional subtraction of the group order n.

Used for the FIPS 186-4 hash truncation and for folding a curve x-coordinate (which lives in [0, p) < 2n) into a scalar.

Template parameters
C

the curve traits.

Parameters
v

the value, < 2n.

Returns

v mod n.

Complexity

O(1).

Allocation

none.

fn fe< C > hash_to_scalar(const std::string &h) source#

Reduce a big-endian hash to a scalar in [0, n).

A hash of at least kBytes keeps its leftmost kBytes (the FIPS 186-4 leftmost-bits truncation); a SHORTER hash is the whole value (X9.62 bits2int — right-aligned), e.g. a SHA-256 signature under a P-384 key.

Template parameters
C

the curve traits.

Parameters
h

the digest bytes.

Returns

the scalar in [0, n).

Complexity

O(1) — at most kBytes are copied regardless of the hash length.

Allocation

none — a stack buffer.

fn bool der_to_rs(const std::string &der, unsigned char *r, unsigned char *s) source#

Parse SEQUENCE{INTEGER r, INTEGER s} -> kBytes big-endian r and s.

Short-form lengths only: both curves' SEQUENCE stays under 128 bytes (P-384: <= ~104).

Template parameters
C

the curve traits.

Parameters
der

the DER-encoded signature.

r

receives kBytes big-endian r.

s

receives kBytes big-endian s.

Returns

false on any malformed encoding.

Complexity

O(1) — short-form DER caps the accepted input at 129 bytes (a longer der fails the exact-length check without being scanned).

Allocation

none.

fn bool on_curve(const fe< C > &x, const fe< C > &y) source#

Is (x, y) on the curve y^2 = x^3 - 3x + b (mod p)?

Rejects an off-curve / invalid-curve public key — SP 800-56A / FIPS 186 point validation, which the plain coordinate-range check (x,y < p) does not catch.

Template parameters
C

the curve traits.

Parameters
x

the affine x-coordinate (plain form, < p).

y

the affine y-coordinate (plain form, < p).

Returns

true iff the point satisfies the curve equation.

Complexity

O(1) — a fixed handful of field operations.

Allocation

none.

fn bool verify_raw(const std::string &pubkey_xy, const std::string &msg_hash, const std::string &sig_raw) source#

ECDSA verification over raw byte forms: pubkey = 2*kBytes X||Y, sig = 2*kBytes r||s.

Template parameters
C

the curve traits.

Parameters
pubkey_xy

the public key point, 2*kBytes X||Y big-endian.

msg_hash

the message digest (truncated/reduced by hash_to_scalar).

sig_raw

the signature, 2*kBytes r||s big-endian.

Returns

true iff the signature verifies (range checks, on-curve check, and x == r all pass).

Complexity

O(1) — two scalar multiplications, computed as one Strauss-Shamir double chain.

Allocation

none.

fn bool verify_der(const std::string &pubkey_xy, const std::string &msg_hash, const std::string &sig_der) source#

ECDSA verification of the DER form (SEQUENCE{INTEGER r, INTEGER s} — TLS/X.509).

Template parameters
C

the curve traits.

Parameters
pubkey_xy

the public key point, 2*kBytes X||Y big-endian.

msg_hash

the message digest.

sig_der

the DER-encoded signature.

Returns

true iff the DER parses and the signature verifies.

Complexity

O(1) — der_to_rs plus one verify_raw.

Allocation

a temporary raw r||s signature string.

fn std::string rs_to_der(const std::string &sig_raw) source#

Encode a raw r||s signature (2*kBytes big-endian bytes) as the DER SEQUENCE{INTEGER r, INTEGER s} that TLS CertificateVerify and X.509 carry — the exact inverse of der_to_rs.

Integers are minimal-form: leading zero bytes are stripped and a 0x00 sign byte is prepended when the top bit is set, so the output round-trips through any strict DER parser. The outer length always fits short form (max 2*(kBytes+3) = 102 bytes at P-384).

Template parameters
C

the curve traits.

Parameters
sig_raw

the 2*kBytes r||s signature (e.g. sign_raw's output).

Returns

the DER bytes, or "" if sig_raw has the wrong length or a zero integer (r = 0 / s = 0 is never a valid ECDSA signature).

Complexity

O(kBytes).

Allocation

the returned string plus the two integer temporaries.

Constants & variables

var std::size_t kBits source#
var std::size_t kBytes source#

Types

type std::uint64_t u64 source#
type unsigned __int128 u128 source#
type std::array< u64, C::kLimbs > fe source#