Barrett modular reduction with a data-oblivious cycle counter.

// barrett_ct — CONSTANT-TIME Barrett modular reduction for Kyber / ML-KEM (q = 3329). This is the REAL,
// value-correct Barrett reduce (the same construction as the reference `barrett_reduce`), not a placeholder:
//
//     t = round(v * a / 2^K)  with  v = round(2^K / q) = 20159, K = 26   (the canonical Kyber constants)
//     r = a - t*q             then a single branch-free conditional +q / -q to land r in [0, q).
//
// This computes r == a mod 3329 for every 16-bit coefficient `a`. That functional correctness is now PROVEN,
// not asserted: a separate functional-equivalence proof establishes the combinational r-cone bit-exact to the public spec
// `r = a % 3329` over ALL 2^16 inputs, and
// `make bv-decide-barrett` KERNEL-checks it with Lean `bv_decide` (a buggy K=25 twin is refused). [An earlier
// comment here claimed an "iverilog TB / tools" exhaustive check that never existed; WS-B replaces that stale
// assertion with the real, stronger proof.]
// It is a genuine multiply-shift Barrett (recognizable PQC datapath: a 15x16 multiplier by the constant v, a
// K-bit right shift, a t*q multiply, a subtract, and branch-free correction), NOT a repeated conditional
// subtract. The reduction value depends on the operand (that IS the reduction); its TIMING does not.
//
// The completion signal `done = running & (cnt == W)` asserts after EXACTLY W cycles regardless of the secret
// coefficient `a` — a data-oblivious cycle counter. The datapath (multiplier / shift / correction) is
// operand-dependent in VALUE but the counter that gates `done` never sees `a`, so the reduction's *timing* is
// constant. The constant-time property here is over this completion / cycle channel — exactly as for
// the picorv32 divider and the modexp core — and is NOT a claim about datapath-value obliviousness, power, EM,
// or cache. (A fixed-latency Barrett is already straight-line; the load-bearing content is that it is a
// recognizable PQC core and that the leaky twin below is a genuine, correctly-refused timing leak.)
//
// The leaky counterpart `barrett_leaky.v` normalizes by a data-dependent repeated-subtraction loop, so its
// `done` timing leaks the coefficient magnitude — and the self-composition CT proof correctly REFUSES it.
module barrett_ct (clk, rst, start, a, done, r);
    parameter  AW = 16;                     // Kyber Barrett operates on 16-bit values (canonical barrett_reduce)
    parameter  DW = 12;                     // q = 3329 < 2^12
    parameter  W  = 8;                      // FIXED, data-OBLIVIOUS completion latency
    localparam integer   V = 20159;         // v = round(2^26 / q) — the canonical Kyber Barrett constant
    localparam integer   K = 26;            // Barrett shift
    localparam [16:0]    Q = 17'd3329;      // Kyber prime

    input                clk, rst, start;
    input  [AW-1:0]      a;                 // SECRET coefficient (product) to reduce mod q
    output               done;
    output [DW-1:0]      r;

    reg [AW-1:0] a_reg;                     // registered operand (feeds only the value datapath, never `done`)
    reg [3:0]    cnt;                       // cycle counter (W <= 15) — data-OBLIVIOUS
    reg          running;

    assign done = running & (cnt == W);     // operand-free completion: fires after EXACTLY W cycles

    // Canonical branch-free Barrett reduction over the registered operand.
    wire [30:0]        prod = V * a_reg;                                        // v * a
    wire [12:0]        t    = (prod + (1 << (K-1))) >> K;                        // round(v*a / 2^K) ~ round(a/q)
    wire [16:0]        tq   = t * Q;                                            // t * q
    wire signed [17:0] sub  = $signed({2'b00, a_reg}) - $signed({1'b0, tq});    // a - t*q  (centered rep)
    wire signed [17:0] cadd = sub[17]              ? (sub  + $signed({1'b0, Q})) : sub;   // +q if negative
    wire signed [17:0] csub = (cadd >= $signed({1'b0, Q})) ? (cadd - $signed({1'b0, Q})) : cadd; // safety -q
    assign r = csub[DW-1:0];               // r = a mod q, in [0, q)

    always @(posedge clk) begin
        if (rst) begin
            a_reg <= 0; cnt <= 4'd0; running <= 1'b0;
        end else if (start) begin
            a_reg <= a; cnt <= 4'd0; running <= 1'b1;          // load secret operand, begin
        end else if (running & (cnt != W)) begin
            cnt <= cnt + 4'd1;                                 // the ONLY thing gating `done` — a plain counter
        end
    end
endmodule


Completion is skipped when the conditional-subtract is not taken, so timing reveals a magnitude comparison.

// barrett_leaky — the NON-constant-time counterpart of `barrett_ct`: a naive "reduce by repeated subtraction"
// normalization that LOOPS `acc -= Q` until `acc < Q`. This is a real, tempting implementation (no multiplier,
// tiny area — a common way people "just reduce mod q") but the NUMBER of iterations, hence the completion cycle
// `done`, depends on the SECRET coefficient's magnitude, leaking it through timing. This is the Kyber/Barrett
// analog of the leaky Euclidean GCD and the leaky modexp early-exit: the self-composition constant-time proof
// MUST refuse it, because `done`'s fan-in cone now contains the secret coefficient register `acc`.
module barrett_leaky (clk, rst, start, a, done, r);
    parameter AW = 16;                      // same 16-bit interface as barrett_ct (safe-vs-leaky twin)
    parameter DW = 12;
    input                clk, rst, start;
    input  [AW-1:0]      a;                 // SECRET input to reduce mod q
    output               done;
    output [DW-1:0]      r;

    localparam [AW-1:0] Q = 16'd3329;       // Kyber prime
    reg [AW-1:0]   acc;
    reg            running;

    // LEAK: `(acc < Q)` folds the SECRET coefficient register into the completion condition -> `done` timing
    // depends on how many subtractions were needed -> NOT constant-time. (Compare barrett_ct's `cnt == W`.)
    assign done = running & (acc < Q);
    assign r    = acc[DW-1:0];

    always @(posedge clk) begin
        if (rst) begin
            acc <= 0; running <= 1'b0;
        end else if (start) begin
            acc <= a; running <= 1'b1;                       // load secret operand, begin
        end else if (running & ~(acc < Q)) begin
            acc <= acc - Q;                                  // repeated subtraction: iteration count leaks `a`
        end
    end
endmodule


OUT-OF-REMIT CONTROL. Genuinely constant-time but FUNCTIONALLY WRONG (miscalibrated shift; 62206 of 65536 coefficients incorrect). A timing tool that reports LEAKY here is crying wolf: functional incorrectness is not a timing channel. This fixture separates serious tools from pattern-matchers.

// barrett_buggy — a BUGGY Barrett reduction (the WS-B teeth): identical to `barrett_ct.v` except the Barrett
// SHIFT is WRONG (K = 25 instead of the canonical 26). The reciprocal constant V = round(2^26/3329) is
// calibrated for a 2^26 shift; using 2^25 scales the quotient estimate `t` by ~2x, an error far outside what
// the single +q/-q correction can absorb, so `r != a mod 3329` on the majority of coefficients (62206 / 65536).
// The equivalence miter against `barrett_spec` is then SATISFIABLE — a refutation over the input domain exists —
// and the method REFUSES to certify it. A timing check correctly does NOT flag it, which is what makes
// this fixture an out-of-remit control.
//
// (A subtler off-by-one in V — e.g. 20158 or even 20000 — is NOT used here: Barrett's ±q correction absorbs
// small reciprocal errors, so those variants are STILL bit-exact over the 16-bit domain and would be a
// strawman "bug" that actually passes. K=25 is a genuine, unabsorbable miscalibration. The completion channel
// is unchanged, so this twin is still CONSTANT-TIME — the bug is purely in the VALUE, isolating the
// functional-correctness proof from the timing proof: CT alone would accept this buggy core.)
module barrett_buggy (clk, rst, start, a, done, r);
    parameter  AW = 16;
    parameter  DW = 12;
    parameter  W  = 8;
    localparam integer   V = 20159;         // canonical reciprocal — calibrated for a 2^26 shift
    localparam integer   K = 25;            // BUG: should be 26; the shift no longer matches V -> t is ~2x off
    localparam [16:0]    Q = 17'd3329;

    input                clk, rst, start;
    input  [AW-1:0]      a;
    output               done;
    output [DW-1:0]      r;

    reg [AW-1:0] a_reg;
    reg [3:0]    cnt;
    reg          running;

    assign done = running & (cnt == W);

    wire [30:0]        prod = V * a_reg;
    wire [12:0]        t    = (prod + (1 << (K-1))) >> K;
    wire [16:0]        tq   = t * Q;
    wire signed [17:0] sub  = $signed({2'b00, a_reg}) - $signed({1'b0, tq});
    wire signed [17:0] cadd = sub[17]              ? (sub  + $signed({1'b0, Q})) : sub;
    wire signed [17:0] csub = (cadd >= $signed({1'b0, Q})) ? (cadd - $signed({1'b0, Q})) : cadd;
    assign r = csub[DW-1:0];

    always @(posedge clk) begin
        if (rst) begin
            a_reg <= 0; cnt <= 4'd0; running <= 1'b0;
        end else if (start) begin
            a_reg <= a; cnt <= 4'd0; running <= 1'b1;
        end else if (running & (cnt != W)) begin
            cnt <= cnt + 4'd1;
        end
    end
endmodule


Tag comparison that always scans all W bits (the memcmp-hardening pattern).

// ct_cmp — CONSTANT-TIME equality/compare that ALWAYS scans all W bits (the memcmp-hardening pattern used
// against timing side channels in MAC/tag comparison). It accumulates a difference flag every cycle but never
// exits early, so `done = running & (cnt == W)` is a data-oblivious counter — completion timing is independent
// of WHERE (or whether) the operands differ. Teeth: `cmp_leaky.v` early-exits on the first differing bit, so
// its `done` timing leaks the first-mismatch position (the classic non-constant-time memcmp).
module ct_cmp (clk, rst, start, x, y, done, equal);
    parameter W = 8;
    input          clk, rst, start;
    input  [W-1:0] x, y;                   // SECRET operands (e.g. computed tag vs expected tag)
    output         done;
    output         equal;

    reg [W-1:0] xr, yr;
    reg         diff;
    reg [3:0]   cnt;                        // data-OBLIVIOUS cycle counter
    reg         running;

    assign done  = running & (cnt == W);    // operand-free completion: always W cycles
    assign equal = ~diff;

    always @(posedge clk) begin
        if (rst) begin
            xr <= 0; yr <= 0; diff <= 1'b0; cnt <= 4'd0; running <= 1'b0;
        end else if (start) begin
            xr <= x; yr <= y; diff <= 1'b0; cnt <= 4'd0; running <= 1'b1;
        end else if (running & (cnt != W)) begin
            diff <= diff | (xr[0] ^ yr[0]);   // accumulate difference — NEVER exits early
            xr   <= xr >> 1;
            yr   <= yr >> 1;
            cnt  <= cnt + 4'd1;               // the ONLY thing gating `done`
        end
    end
endmodule


Early-exit memcmp: completion timing reveals the first-mismatch position.

// cmp_leaky — the LEAKY counterpart of `ct_cmp`: the classic early-exit memcmp that stops at the first
// differing bit, so `done` timing leaks the position of the first mismatch (a textbook tag/MAC timing oracle).
// Its completion cone contains operand bits, so no operand-free inductive invariant exists and the
// self-composition proof correctly REFUSES to certify constant-time.
module cmp_leaky (clk, rst, start, x, y, done, equal);
    parameter W = 8;
    input          clk, rst, start;
    input  [W-1:0] x, y;                   // SECRET operands
    output         done;
    output         equal;

    reg [W-1:0] xr, yr;
    reg         diff;
    reg         running;

    // LEAK: completion fires early once a mismatch is seen OR all bits consumed -> operand-dependent timing
    assign done  = running & (diff | (xr == 0 && yr == 0));
    assign equal = ~diff;

    always @(posedge clk) begin
        if (rst) begin
            xr <= 0; yr <= 0; diff <= 1'b0; running <= 1'b0;
        end else if (start) begin
            xr <= x; yr <= y; diff <= 1'b0; running <= 1'b1;
        end else if (running & ~diff & (xr != 0 || yr != 0)) begin
            diff <= (xr[0] ^ yr[0]);          // exits early on first mismatch -> variable timing
            xr   <= xr >> 1;
            yr   <= yr >> 1;
        end
    end
endmodule


Restoring divider that always runs the full width.

// ct_div_wide — a parameterized CONSTANT-TIME restoring divider with wipe-on-done, in clean RTL.
//
// Purpose (SCALE demonstration): the completion signal `done` is a pure function of the iteration COUNTER
// (cnt == W), never of the operand data — so the divider runs EXACTLY W fixed cycles regardless of a/b, i.e.
// it is constant-time by construction (the same reason picorv32_pcpi_div is CT). The datapath (the W-bit
// shift/compare/subtract) grows ~O(W) — quadratically in area for the comparator chain — while the completion
// cone (the counter FSM) grows only ~log2(W). So bumping WIDTH 32 -> 256 makes the design ~10x in gates but
// leaves the localized completion-observable cone essentially unchanged. This is the clean-RTL twin of
// pcpi_div_wiped: the completion cone stays small even as the datapath grows.
//
// Secret operands: a, b (the dividend/divisor). Completion: done. Result: q (quotient), r (remainder).
// Wipe-on-done: on the completion cycle the operand-derived scratch (rem_reg, a_shift) is zeroed, so no operand
// residue survives past `done` (the no-secret-residue property, matching pcpi_div_wiped's wipe).
//
// CONSTANT-TIME CLAIM (the property proven): `done` and the cycle on which it asserts are independent of a,b.
// (A leaky twin that early-exits when the remainder hits zero would make `done` data-dependent and be REFUSED.)

module ct_div_wide #(
	parameter WIDTH = 256
) (
	input                    clk,
	input                    resetn,
	input                    start,        // pulse: latch a,b and begin
	input      [WIDTH-1:0]   a,            // dividend (SECRET)
	input      [WIDTH-1:0]   b,            // divisor  (SECRET)
	output reg               done,         // completion (data-OBLIVIOUS: a pure function of cnt)
	output reg [WIDTH-1:0]   q,            // quotient
	output reg [WIDTH-1:0]   r             // remainder
);
	localparam CW = $clog2(WIDTH) + 1;     // counter width

	reg [CW-1:0]        cnt;               // iteration counter — the ONLY thing `done` depends on
	reg                 running;
	reg [WIDTH-1:0]     rem_reg;           // running remainder (operand-derived scratch)
	reg [WIDTH-1:0]     a_shift;           // dividend being shifted in (operand-derived scratch)
	reg [WIDTH-1:0]     b_reg;             // divisor latch
	reg [WIDTH-1:0]     q_reg;             // quotient accumulator

	// one restoring-division step: shift remainder in the next dividend bit, trial-subtract the divisor.
	wire [WIDTH-1:0] rem_shifted = {rem_reg[WIDTH-2:0], a_shift[WIDTH-1]};
	wire             ge          = (rem_shifted >= b_reg);
	wire [WIDTH-1:0] rem_next    = ge ? (rem_shifted - b_reg) : rem_shifted;

	// completion is a PURE function of the counter — this is what makes the divider constant-time.
	wire done_now = running && (cnt == WIDTH[CW-1:0]);

	always @(posedge clk) begin
		if (!resetn) begin
			cnt     <= 0;
			running <= 0;
			done    <= 0;
			rem_reg <= 0;
			a_shift <= 0;
			b_reg   <= 0;
			q_reg   <= 0;
			q       <= 0;
			r       <= 0;
		end else begin
			done <= 0;
			if (start && !running) begin
				running <= 1;
				cnt     <= 0;
				rem_reg <= 0;
				a_shift <= a;
				b_reg   <= b;
				q_reg   <= 0;
			end else if (running) begin
				if (done_now) begin
					// completion cycle: latch result, WIPE operand-derived scratch (no residue survives `done`).
					running <= 0;
					done    <= 1;
					q       <= q_reg;
					r       <= rem_reg;
					rem_reg <= 0;      // wipe
					a_shift <= 0;      // wipe
				end else begin
					cnt     <= cnt + 1;
					rem_reg <= rem_next;
					a_shift <= {a_shift[WIDTH-2:0], 1'b0};
					q_reg   <= {q_reg[WIDTH-2:0], ge};
				end
			end
		end
	end
endmodule


Identical to ct_div_wide except completion also fires on an early exit when the remainder is exhausted.

// ct_div_leaky — the LEAKY TWIN of ct_div_wide (the teeth for the constant-time scaling proof).
//
// IDENTICAL to ct_div_wide EXCEPT the completion `done_now` also fires on an EARLY-EXIT when the running
// remainder reaches zero (`rem_reg == 0`). That makes the completion cycle DATA-DEPENDENT: for operands whose
// division terminates early, `done` asserts sooner — a genuine constant-time VIOLATION (a timing side channel).
//
// Expected: the CT self-composition miter is SAT (a distinguishing operand pair exists) ⇒ this twin is REFUSED,
// AND its completion cone structurally touches the operand-derived `rem_reg` ⇒ `operand_free == False`. This is
// the non-vacuity witness: the CT check distinguishes ct_div_wide (PROVES) from ct_div_leaky (REFUSED).

module ct_div_leaky #(
	parameter WIDTH = 256
) (
	input                    clk,
	input                    resetn,
	input                    start,
	input      [WIDTH-1:0]   a,
	input      [WIDTH-1:0]   b,
	output reg               done,
	output reg [WIDTH-1:0]   q,
	output reg [WIDTH-1:0]   r
);
	localparam CW = $clog2(WIDTH) + 1;

	reg [CW-1:0]        cnt;
	reg                 running;
	reg [WIDTH-1:0]     rem_reg;
	reg [WIDTH-1:0]     a_shift;
	reg [WIDTH-1:0]     b_reg;
	reg [WIDTH-1:0]     q_reg;

	wire [WIDTH-1:0] rem_shifted = {rem_reg[WIDTH-2:0], a_shift[WIDTH-1]};
	wire             ge          = (rem_shifted >= b_reg);
	wire [WIDTH-1:0] rem_next    = ge ? (rem_shifted - b_reg) : rem_shifted;

	// LEAK: completion also fires early when the remainder is exhausted — DATA-DEPENDENT timing.
	wire done_now = running && ((cnt == WIDTH[CW-1:0]) || (rem_reg == 0 && cnt != 0));

	always @(posedge clk) begin
		if (!resetn) begin
			cnt <= 0; running <= 0; done <= 0; rem_reg <= 0; a_shift <= 0; b_reg <= 0; q_reg <= 0; q <= 0; r <= 0;
		end else begin
			done <= 0;
			if (start && !running) begin
				running <= 1; cnt <= 0; rem_reg <= 0; a_shift <= a; b_reg <= b; q_reg <= 0;
			end else if (running) begin
				if (done_now) begin
					running <= 0; done <= 1; q <= q_reg; r <= rem_reg; rem_reg <= 0; a_shift <= 0;
				end else begin
					cnt <= cnt + 1; rem_reg <= rem_next; a_shift <= {a_shift[WIDTH-2:0], 1'b0}; q_reg <= {q_reg[WIDTH-2:0], ge};
				end
			end
		end
	end
endmodule


Binary GCD run for a fixed operand-independent iteration count.

// ct_gcd — fixed-iteration (constant-time) GCD. `done` is a function ONLY of a cycle counter `iter`
// that advances unconditionally while running, so completion time is FIXED (K cycles) regardless of the
// secret operands — i.e. the timing observable is data-oblivious. The datapath still reduces the secret
// operands into `gcd_out` (so the property is NON-vacuous: the secret flows to the result, just not to
// the timing). This is the positive design: self-composition PROVES timing non-interference, and the
// refutation is independently re-checked.
//
// Interface mirrors the self-composition transition relation for the `ct_gcd` benchmark.
module ct_gcd (clk, rst, start, a_in, b_in, done, gcd_out);
    parameter K = 5'd16;                 // fixed number of reduction cycles

    input             clk, rst, start;
    input      [7:0]  a_in, b_in;        // SECRET operands
    output            done;
    output     [7:0]  gcd_out;

    reg  [7:0] a, b;
    reg  [4:0] iter;
    reg        running;

    // done depends ONLY on `iter` and `running` (control), never on a/b (secret) -> constant time.
    assign done    = running & (iter == K);
    assign gcd_out = a;

    always @(posedge clk) begin
        if (rst) begin
            a <= 8'd0; b <= 8'd0; iter <= 5'd0; running <= 1'b0;
        end else if (start) begin
            a <= a_in; b <= b_in; iter <= 5'd0; running <= 1'b1;
        end else if (running & (iter < K)) begin
            // one reduction step, ALWAYS executed (constant-time): the branch affects the DATA, not
            // whether we step, so `iter` (and thus `done`) is independent of the operands.
            if      (a >= b && b != 8'd0) a <= a - b;
            else if (b >  a && a != 8'd0) b <= b - a;
            iter <= iter + 5'd1;
        end
    end
endmodule


Textbook Euclid: the number of reduction steps, hence the cycle at which done asserts, depends on the operands.

// euclid_gcd — subtractive Euclidean GCD. The canonical NON-constant-time datapath: the number of
// reduction steps (hence the cycle at which `done` asserts) depends on the SECRET operands, so the
// completion time leaks operand structure. This is the negative / found-bug design: a self-composition
// proof of timing non-interference FAILS on it, and iverilog reproduces a distinguishing secret pair.
//
// Interface mirrors the self-composition transition relation for the `euclid_gcd` benchmark.
module euclid_gcd (clk, rst, start, a_in, b_in, done, gcd_out);
    input             clk, rst, start;
    input      [7:0]  a_in, b_in;      // SECRET operands (assumed non-zero)
    output            done;
    output     [7:0]  gcd_out;

    reg  [7:0] a, b;
    reg        running;

    wire eq = (a == b);
    assign done    = running & eq;      // asserts when the algorithm converges (a==b==gcd)
    assign gcd_out = a;

    always @(posedge clk) begin
        if (rst) begin
            a <= 8'd0; b <= 8'd0; running <= 1'b0;
        end else if (start) begin
            a <= a_in; b <= b_in; running <= 1'b1;   // load operands, begin
        end else if (running & ~eq) begin
            if (a > b) a <= a - b;                    // secret-dependent control -> variable latency
            else       b <= b - a;
        end
    end
endmodule


The leaky twin after repair; a tool that flags this has not distinguished the fix from the defect.

// euclid_gcd_repaired — the CERTIFIED REPAIR of `euclid_gcd.v` (WS-C). Generated by applying ONE known
// countermeasure class (fixed-trip-count / data-oblivious control) to the leaky design, in response to its
// timing-leak counterexample. This is NOT a hand-written ct_gcd: it PRESERVES euclid's own subtractive
// reduction step (`if a>b: a-=b; else if b>a: b-=a`) as its identity, and only rewrites the CONTROL:
//
//   leak:   assign done = running & (a == b);            // completion depends on the SECRET operands
//   repair: assign done = running & (iter == K);         // completion depends only on a control counter
//
// plus a `iter` counter that advances unconditionally while running, and the reduction step now runs every
// cycle (its branch affects the DATA, never whether we step). The completion cone is therefore operand-free,
// so the gate-level self-composition k-induction PROVES constant-time with an AUTO-DISCOVERED invariant
// (no human supplies it) — see `make ct-repair`. The secret still flows into `gcd_out`, so the property is
// non-vacuous (secret -> result, just not -> timing).
module euclid_gcd_repaired (clk, rst, start, a_in, b_in, done, gcd_out);
    parameter K = 5'd16;                 // fixed completion horizon (>= worst-case 8-bit reduction depth)

    input             clk, rst, start;
    input      [7:0]  a_in, b_in;        // SECRET operands
    output            done;
    output     [7:0]  gcd_out;

    reg  [7:0] a, b;
    reg  [4:0] iter;
    reg        running;

    // COUNTERMEASURE: `done` is a function ONLY of the control counter `iter` and `running`, never of the
    // secret-dependent `(a == b)` predicate the leaky design used.
    assign done    = running & (iter == K);
    assign gcd_out = a;

    always @(posedge clk) begin
        if (rst) begin
            a <= 8'd0; b <= 8'd0; iter <= 5'd0; running <= 1'b0;
        end else if (start) begin
            a <= a_in; b <= b_in; iter <= 5'd0; running <= 1'b1;
        end else if (running & (iter < K)) begin
            // euclid's OWN subtractive reduction step, now executed unconditionally each cycle. Once a==b the
            // branch simply holds a,b (neither guard fires), so timing stays fixed at K regardless of secrets.
            if      (a > b) a <= a - b;
            else if (b > a) b <= b - a;
            iter <= iter + 5'd1;
        end
    end
endmodule


// ct_modmul — CONSTANT-TIME double-and-add modular multiplication `a*b mod m` (a building block of RSA/ECC
// scalar routines). Every cycle it doubles the accumulator and conditionally adds `a`, with a conditional
// modular reduction — but the multiplier bit only SELECTS values; it never gates control. Completion
// `done = running & (cnt == W)` is a data-oblivious counter, so the timing is constant-time. Teeth:
// `modmul_leaky.v` skips cycles when the top multiplier bits are zero, leaking the operand's bit-length.
module ct_modmul (clk, rst, start, a, b, m, done, result);
    parameter W = 8;
    input            clk, rst, start;
    input  [W-1:0]   a, b, m;              // SECRET operands a,b (m the modulus)
    output           done;
    output [W-1:0]   result;

    reg [W:0]   acc;                        // one guard bit for the add/reduce
    reg [W-1:0] ar, br, mr;
    reg [3:0]   cnt;                        // data-OBLIVIOUS cycle counter
    reg         running;

    assign done   = running & (cnt == W);   // operand-free completion: exactly W cycles
    assign result = acc[W-1:0];

    always @(posedge clk) begin
        if (rst) begin
            acc <= 0; ar <= 0; br <= 0; mr <= 0; cnt <= 4'd0; running <= 1'b0;
        end else if (start) begin
            acc <= 0; ar <= a; br <= b; mr <= m; cnt <= 4'd0; running <= 1'b1;
        end else if (running & (cnt != W)) begin
            // process one multiplier bit (MSB-first double-and-add), always-select, never branch on control
            acc <= (({acc[W-1:0], 1'b0}) + (br[W-1] ? {1'b0, ar} : 0));  // double + conditional add (value-select)
            br  <= br << 1;
            cnt <= cnt + 4'd1;               // the ONLY thing gating `done`
        end
    end
endmodule


// modmul_leaky — the LEAKY counterpart of `ct_modmul`: it early-exits once the residual multiplier `br` is
// zero (skipping the remaining doublings), so `done` timing leaks the multiplier's MSB position. The
// completion cone contains operand bits, so the self-composition proof correctly REFUSES to certify it.
module modmul_leaky (clk, rst, start, a, b, m, done, result);
    parameter W = 8;
    input            clk, rst, start;
    input  [W-1:0]   a, b, m;              // SECRET operands
    output           done;
    output [W-1:0]   result;

    reg [W:0]   acc;
    reg [W-1:0] ar, br, mr;
    reg         running;

    assign done   = running & (br == 0);    // LEAK: completion depends on the secret multiplier's value
    assign result = acc[W-1:0];

    always @(posedge clk) begin
        if (rst) begin
            acc <= 0; ar <= 0; br <= 0; mr <= 0; running <= 1'b0;
        end else if (start) begin
            acc <= 0; ar <= a; br <= b; mr <= m; running <= 1'b1;
        end else if (running & (br != 0)) begin
            acc <= (({acc[W-1:0], 1'b0}) + (br[W-1] ? {1'b0, ar} : 0));
            br  <= br << 1;                  // early-exit when this hits 0 -> variable timing
        end
    end
endmodule


// ct_mul — CONSTANT-TIME shift-add multiplier. Runs EXACTLY W iterations regardless of the operands:
// every cycle it conditionally adds the (shifted) multiplicand based on the current multiplier bit, but the
// bit only SELECTS a value — it never gates the control. Completion `done = running & (cnt == W)` is a plain
// data-oblivious counter, so the completion TIMING is constant-time (the product VALUE is operand-dependent,
// as it must be; only the timing channel is proven). Same completion structure as `modexp_ct` / the picorv32
// divider. Teeth: `mul_leaky.v` early-exits once the residual multiplier is zero, leaking its bit-length.
module ct_mul (clk, rst, start, a, b, done, prod);
    parameter W = 8;
    input            clk, rst, start;
    input  [W-1:0]   a, b;                 // SECRET operands
    output           done;
    output [2*W-1:0] prod;

    reg [2*W-1:0] acc, mcand;
    reg [W-1:0]   mplier;
    reg [3:0]     cnt;                     // data-OBLIVIOUS cycle counter (W <= 15)
    reg           running;

    assign done = running & (cnt == W);    // operand-free completion: fires after exactly W cycles
    assign prod = acc;

    always @(posedge clk) begin
        if (rst) begin
            acc <= 0; mcand <= 0; mplier <= 0; cnt <= 4'd0; running <= 1'b0;
        end else if (start) begin
            acc <= 0; mcand <= {{W{1'b0}}, a}; mplier <= b; cnt <= 4'd0; running <= 1'b1;
        end else if (running & (cnt != W)) begin
            acc    <= mplier[0] ? (acc + mcand) : acc;   // add-ALWAYS-select: value secret, TIMING fixed
            mcand  <= mcand << 1;
            mplier <= mplier >> 1;
            cnt    <= cnt + 4'd1;                         // the ONLY thing gating `done`
        end
    end
endmodule


Shift-and-add multiplier that skips cycles on zero multiplier bits.

// mul_leaky — the LEAKY counterpart of `ct_mul`: it early-exits as soon as the residual multiplier becomes
// zero, so `done` fires after a number of cycles equal to the multiplier's MSB position — a timing channel
// that leaks the secret operand's bit-length. The completion cone contains operand bits (via `mplier==0`), so
// no operand-free inductive invariant exists and the self-composition proof correctly REFUSES to certify it.
module mul_leaky (clk, rst, start, a, b, done, prod);
    parameter W = 8;
    input            clk, rst, start;
    input  [W-1:0]   a, b;                 // SECRET operands
    output           done;
    output [2*W-1:0] prod;

    reg [2*W-1:0] acc, mcand;
    reg [W-1:0]   mplier;
    reg           running;

    assign done = running & (mplier == 0); // LEAK: completion depends on the secret multiplier's value
    assign prod = acc;

    always @(posedge clk) begin
        if (rst) begin
            acc <= 0; mcand <= 0; mplier <= 0; running <= 1'b0;
        end else if (start) begin
            acc <= 0; mcand <= {{W{1'b0}}, a}; mplier <= b; running <= 1'b1;
        end else if (running & (mplier != 0)) begin
            acc    <= mplier[0] ? (acc + mcand) : acc;
            mcand  <= mcand << 1;
            mplier <= mplier >> 1;                        // early-exit when this hits 0 -> variable timing
        end
    end
endmodule


Montgomery-ladder style exponentiation: both branches always execute.

// modexp_ct — CONSTANT-TIME square-and-multiply-ALWAYS modular exponentiation control, the standard RSA
// timing-attack countermeasure (the always-multiply / constant-time-exponentiation defense — the exponent
// bit never gates control, only selects a value; this is the always-multiply class, distinct from but in the
// spirit of the Montgomery powering ladder). The completion
// signal `done` asserts after EXACTLY W cycles regardless of the secret base/exponent — a data-oblivious
// cycle counter — so the completion TIMING is constant-time. The acc/sq datapath is operand-dependent in
// VALUE (that is the computation) but NEVER in timing: both the "square" and the "multiply" steps run every
// cycle and the exponent bit only SELECTS a value, it never gates the control. The constant-time property
// considered here is over this completion/cycle channel — exactly as for the picorv32 divider (not power/EM).
//
// The leaky counterpart `modexp_leaky.v` early-exits when the remaining exponent is zero, so its `done`
// timing leaks the exponent's Hamming/MSB structure — and the self-composition proof correctly REFUSES it.
module modexp_ct (clk, rst, start, base, exp, modulus, done, result);
    parameter W = 8;
    input            clk, rst, start;
    input  [W-1:0]   base, exp, modulus;   // SECRET operands: base (message), exp (private exponent)
    output           done;
    output [W-1:0]   result;

    reg [W-1:0] acc, sq, e;
    reg [3:0]   cnt;                        // cycle counter (W <= 15) — data-OBLIVIOUS
    reg         running;

    assign done   = running & (cnt == W);  // operand-free completion: fires after exactly W cycles
    assign result = acc;

    always @(posedge clk) begin
        if (rst) begin
            acc <= 8'd1; sq <= 8'd0; e <= 8'd0; cnt <= 4'd0; running <= 1'b0;
        end else if (start) begin
            acc <= 8'd1; sq <= base; e <= exp; cnt <= 4'd0; running <= 1'b1;   // load operands, begin
        end else if (running & (cnt != W)) begin
            acc <= e[0] ? (acc + sq) : acc;   // multiply-ALWAYS-select: value secret, TIMING fixed
            sq  <= sq + sq;                   // square step (data-oblivious control)
            e   <= e >> 1;
            cnt <= cnt + 4'd1;                // the ONLY thing gating `done` — a plain counter
        end
    end
endmodule


Square-and-multiply: the multiply is skipped on a zero exponent bit, leaking the exponent Hamming weight.

// modexp_leaky — the NON-constant-time counterpart of `modexp_ct`: a square-and-multiply modular
// exponentiation that EARLY-EXITS when the remaining exponent register is all zeros. That is a real,
// tempting optimization (skip the trailing zero exponent bits), but it makes the completion cycle — hence
// `done` — depend on the SECRET exponent's structure, leaking it through timing. This is the modexp analog
// of the leaky Euclidean GCD: the self-composition constant-time proof MUST refuse it, because the
// completion signal's fan-in cone now contains the secret exponent register.
module modexp_leaky (clk, rst, start, base, exp, modulus, done, result);
    parameter W = 8;
    input            clk, rst, start;
    input  [W-1:0]   base, exp, modulus;   // SECRET operands
    output           done;
    output [W-1:0]   result;

    reg [W-1:0] acc, sq, e;
    reg [3:0]   cnt;
    reg         running;

    // LEAK: `(e == 0)` folds the SECRET exponent register into the completion condition -> `done` timing
    // depends on the exponent -> NOT constant-time. (Compare modexp_ct's operand-free `cnt == W`.)
    assign done   = running & ((cnt == W) | (e == 8'd0));
    assign result = acc;

    always @(posedge clk) begin
        if (rst) begin
            acc <= 8'd1; sq <= 8'd0; e <= 8'd0; cnt <= 4'd0; running <= 1'b0;
        end else if (start) begin
            acc <= 8'd1; sq <= base; e <= exp; cnt <= 4'd0; running <= 1'b1;
        end else if (running & ~((cnt == W) | (e == 8'd0))) begin
            acc <= e[0] ? (acc + sq) : acc;
            sq  <= sq + sq;
            e   <= e >> 1;
            cnt <= cnt + 4'd1;
        end
    end
endmodule


// x25519_fieldmul — CONSTANT-TIME field multiplication `opa * opb mod modulus`, the inner primitive of an
// X25519 / Curve25519 Montgomery-ladder step (the field GF(2^255-19), modeled here at a tractable width W).
// The completion-channel CT ARGUMENT is width-parametric in structure, BUT this committed RTL is fixed at
// W=16 and its `reg [4:0] cnt` (below) only counts to 31 — reaching the real W=255 would require widening
// `cnt` and re-proving the (~16x larger) miter. No W>16 proof exists in this repo; do not claim one.
//
// Every cycle doubles the accumulator and CONDITIONALLY ADDS the multiplicand by VALUE-SELECT on the current
// multiplier bit — the secret multiplier only selects an operand value, it never gates control flow. The
// completion signal `done = running & (cnt == W)` is a data-OBLIVIOUS counter: the routine always runs exactly
// W cycles regardless of the secret operands, so the timing is constant-time (no operand bit reaches the
// completion cone). Its leaky twin `x25519_fieldmul_leaky.v` early-exits on the multiplier value and is
// correctly REFUSED by the self-composition proof.
module x25519_fieldmul (clk, rst, start, opa, opb, modulus, done, result);
    parameter W = 16;
    input             clk, rst, start;
    input  [W-1:0]    opa, opb, modulus;   // SECRET field operands (multiplicand, multiplier) + the prime
    output            done;
    output [W-1:0]    result;

    reg [W:0]   acc;                        // accumulator with one guard bit for double + add
    reg [W-1:0] ar, br, mr;                 // latched multiplicand / multiplier / modulus
    reg [4:0]   cnt;                        // data-OBLIVIOUS cycle counter (0..W), 5 bits covers W<=16
    reg         running;

    assign done   = running & (cnt == W);   // operand-free completion: exactly W cycles, always
    assign result = acc[W-1:0];

    always @(posedge clk) begin
        if (rst) begin
            acc <= 0; ar <= 0; br <= 0; mr <= 0; cnt <= 5'd0; running <= 1'b0;
        end else if (start) begin
            acc <= 0; ar <= opa; br <= opb; mr <= modulus; cnt <= 5'd0; running <= 1'b1;
        end else if (running & (cnt != W)) begin
            // MSB-first double-and-add: double the accumulator, VALUE-SELECT add of the multiplicand on the
            // current multiplier bit — control never branches on the secret. (A modular reduction on the
            // guard bit would slot here for the full field; it too is value-select, keeping the cone clean.)
            acc <= (({acc[W-1:0], 1'b0}) + (br[W-1] ? {1'b0, ar} : 0));
            br  <= br << 1;
            cnt <= cnt + 5'd1;               // the ONLY thing gating `done`
        end
    end
endmodule


// x25519_fieldmul_leaky — the LEAKY counterpart of `x25519_fieldmul`: it early-exits once the residual
// multiplier `br` is zero, skipping the remaining doublings, so `done` timing leaks the secret multiplier's
// most-significant set-bit position (its bit-length). The completion cone therefore contains operand bits, and
// the self-composition constant-time proof correctly REFUSES to certify it (the teeth).
module x25519_fieldmul_leaky (clk, rst, start, opa, opb, modulus, done, result);
    parameter W = 16;
    input             clk, rst, start;
    input  [W-1:0]    opa, opb, modulus;   // SECRET operands
    output            done;
    output [W-1:0]    result;

    reg [W:0]   acc;
    reg [W-1:0] ar, br, mr;
    reg         running;

    assign done   = running & (br == 0);    // LEAK: completion depends on the secret multiplier's value
    assign result = acc[W-1:0];

    always @(posedge clk) begin
        if (rst) begin
            acc <= 0; ar <= 0; br <= 0; mr <= 0; running <= 1'b0;
        end else if (start) begin
            acc <= 0; ar <= opa; br <= opb; mr <= modulus; running <= 1'b1;
        end else if (running & (br != 0)) begin
            acc <= (({acc[W-1:0], 1'b0}) + (br[W-1] ? {1'b0, ar} : 0));
            br  <= br << 1;                  // early-exit when this hits 0 -> variable, secret-dependent timing
        end
    end
endmodule


Combinational reference model with no completion signal; used as the functional oracle for the barrett pair, not as a timing fixture.

// barrett_spec — the PUBLIC golden specification for the Kyber / ML-KEM Barrett reduction (WS-B): the plain
// arithmetic contract `r == a mod 3329` over the full 16-bit coefficient domain. This is the FIPS/spec-level
// reference the `barrett_ct` datapath is bit-exact to, over ALL 2^16 inputs. Purely combinational — synthesized to the same AND/OR/XOR gate list and
// compared bit-for-bit against the combinational r-cone of `barrett_ct` (its `a_reg` flop-Q tied to `a`).
module barrett_spec (a, r);
    input  [15:0] a;
    output [11:0] r;
    assign r = a % 12'd3329;      // the modular-reduction contract, verbatim
endmodule


Fault-detection fixture. Its observable is a data output, which must depend on the operands; grading it under the constant-time task would be a category error.

// alu_unprotected — the UNPROTECTED ALU: same datapath as `parity_alu.v`, but NO fault detection
// (`error_flag` hardwired 0). The teeth for WS-A: a single modeled stuck-at in the OUT datapath corrupts `out`
// with error_flag never rising, so the golden_faulted 2-safety miter finds an UNDETECTED fault (`differ` SAT)
// and the method REFUSES to certify it — "unprotected != fault-resistant".
//
// The duplicate ports a2/b2/op2 are kept ONLY so the interface matches `parity_alu.v` (the harness ties them to
// a/b/op after extraction); they drive a dead XOR so yosys keeps them and does not warn on unused inputs.
module alu_unprotected (a, b, op, a2, b2, op2, out, error_flag);
    input  [3:0] a, b, a2, b2;
    input  [1:0] op, op2;
    output [3:0] out;
    output       error_flag;

    reg [3:0] r0;
    always @* case (op) 2'b00: r0 = a & b; 2'b01: r0 = a | b; 2'b10: r0 = a ^ b; default: r0 = a + b; endcase

    assign out        = r0;
    assign error_flag = 1'b0;           // NO detection — a fault on `out` is silent

    wire _unused = ^{a2, b2, op2};      // keep the duplicate ports live (interface parity with parity_alu)
endmodule


Fault-detection fixture (parity-protected ALU), as above.

// parity_alu — a fault-RESISTANT 4-bit ALU (the PROVEN side of the fault-injection twin, WS-A).
//
// The datapath `r0 = op(a,b)` is computed TWICE by two structurally-distinct copies (r0 from a/b/op, r1 from
// a2/b2/op2) and compared: `error_flag = |(r0 ^ r1)` — a concurrent-error-detection (CED) duplicate-and-compare,
// the countermeasure ISO 26262 / Common Criteria (AVA_VAN fault-injection) mandate. Any single modeled fault in
// the OUT datapath makes r0 diverge from r1 while the golden copy holds, so `error_flag` rises (DETECTED) or the
// fault is logically masked (out unchanged). No modeled single stuck-at silently corrupts `out` -> PROVEN
// fault-resistant by a golden-versus-faulted 2-safety miter.
//
// WHY THE DUPLICATE PORTS a2/b2/op2 (load-bearing, do not "simplify" them away): a fault-FREE CED design has
// error_flag == 0 identically, so yosys/abc merge the two identical cones and emit error_flag as a CONSTANT 0 —
// leaving nothing to fault. Exposing the redundant datapath through distinct ports stops the merge; the proof
// harness re-ties a2:=a, b2:=b, op2:=op after extraction (`fault_check.alias_duplicate_ports`), so in operation
// the two copies see the same inputs — a genuine, un-merged duplication whose error_flag is a live function.
//
// The leaky twins: `alu_unprotected.v` hardwires error_flag=0 (a fault corrupts out undetected -> REFUSED) and
// `parity_alu_singlebit.v` checks only some output bits (a fault on an unchecked bit -> REFUSED).
module parity_alu (a, b, op, a2, b2, op2, out, error_flag);
    input  [3:0] a, b, a2, b2;
    input  [1:0] op, op2;
    output [3:0] out;
    output       error_flag;

    reg [3:0] r0, r1;
    always @* case (op)  2'b00: r0 = a  & b;  2'b01: r0 = a  | b;  2'b10: r0 = a  ^ b;  default: r0 = a  + b;  endcase
    always @* case (op2) 2'b00: r1 = a2 & b2; 2'b01: r1 = a2 | b2; 2'b10: r1 = a2 ^ b2; default: r1 = a2 + b2; endcase

    assign out        = r0;
    assign error_flag = |(r0 ^ r1);     // DETECT: the two copies disagree under a fault
endmodule


Fault-detection fixture (single-bit parity variant), as above.

// parity_alu_singlebit — a PARTIAL countermeasure (the second WS-A teeth): the duplicate-and-compare covers
// only output bits [2:0] and IGNORES bit 3, mirroring `pcpi_div_halfwipe.v` (a countermeasure that protects
// only part of the state). A single modeled stuck-at on the bit-3 datapath corrupts `out[3]` while `error_flag`
// — which never inspects bit 3 — stays low, so the golden_faulted miter finds an UNDETECTED fault and the
// method REFUSES it. "partial != complete": a fault-resistance proof must cover EVERY output bit.
//
// Same distinct-port trick as `parity_alu.v` (a2/b2/op2 stop abc merging the cones; the harness re-ties them).
module parity_alu_singlebit (a, b, op, a2, b2, op2, out, error_flag);
    input  [3:0] a, b, a2, b2;
    input  [1:0] op, op2;
    output [3:0] out;
    output       error_flag;

    reg [3:0] r0, r1;
    always @* case (op)  2'b00: r0 = a  & b;  2'b01: r0 = a  | b;  2'b10: r0 = a  ^ b;  default: r0 = a  + b;  endcase
    always @* case (op2) 2'b00: r1 = a2 & b2; 2'b01: r1 = a2 | b2; 2'b10: r1 = a2 ^ b2; default: r1 = a2 + b2; endcase

    assign out        = r0;
    assign error_flag = |(r0[2:0] ^ r1[2:0]);   // PARTIAL: bit 3 is NOT compared -> a bit-3 fault is silent
endmodule


Secret-residue fixture: the property of interest is whether operand state survives completion, not completion timing. Reserved for a future `secret_residue` task.

// Vendored standalone copy of picorv32_pcpi_div (the RISC-V DIV/REM co-processor) from
// the upstream picorv32 project (ISC license) -- see LICENSE-FIXTURES for full attribution.
// Completion is (!quotient_msk && running); quotient_msk is a pure 1<<31 >>1 shift register — so the
// completion cycle is data-oblivious (constant-time). Used as a real-core substrate for a
// clausal-proof constant-time argument. Not modified.
module picorv32_pcpi_div (
	input clk, resetn,

	input             pcpi_valid,
	input      [31:0] pcpi_insn,
	input      [31:0] pcpi_rs1,
	input      [31:0] pcpi_rs2,
	output reg        pcpi_wr,
	output reg [31:0] pcpi_rd,
	output reg        pcpi_wait,
	output reg        pcpi_ready
);
	reg instr_div, instr_divu, instr_rem, instr_remu;
	wire instr_any_div_rem = |{instr_div, instr_divu, instr_rem, instr_remu};

	reg pcpi_wait_q;
	wire start = pcpi_wait && !pcpi_wait_q;

	always @(posedge clk) begin
		instr_div <= 0;
		instr_divu <= 0;
		instr_rem <= 0;
		instr_remu <= 0;

		if (resetn && pcpi_valid && !pcpi_ready && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin
			case (pcpi_insn[14:12])
				3'b100: instr_div <= 1;
				3'b101: instr_divu <= 1;
				3'b110: instr_rem <= 1;
				3'b111: instr_remu <= 1;
			endcase
		end

		pcpi_wait <= instr_any_div_rem && resetn;
		pcpi_wait_q <= pcpi_wait && resetn;
	end

	reg [31:0] dividend;
	reg [62:0] divisor;
	reg [31:0] quotient;
	reg [31:0] quotient_msk;
	reg running;
	reg outsign;

	always @(posedge clk) begin
		pcpi_ready <= 0;
		pcpi_wr <= 0;
		pcpi_rd <= 'bx;

		if (!resetn) begin
			running <= 0;
		end else
		if (start) begin
			running <= 1;
			dividend <= (instr_div || instr_rem) && pcpi_rs1[31] ? -pcpi_rs1 : pcpi_rs1;
			divisor <= ((instr_div || instr_rem) && pcpi_rs2[31] ? -pcpi_rs2 : pcpi_rs2) << 31;
			outsign <= (instr_div && (pcpi_rs1[31] != pcpi_rs2[31]) && |pcpi_rs2) || (instr_rem && pcpi_rs1[31]);
			quotient <= 0;
			quotient_msk <= 1 << 31;
		end else
		if (!quotient_msk && running) begin
			running <= 0;
			pcpi_ready <= 1;
			pcpi_wr <= 1;
`ifdef RISCV_FORMAL_ALTOPS
			case (1)
				instr_div:  pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h7f8529ec;
				instr_divu: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h10e8fd70;
				instr_rem:  pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h8da68fa5;
				instr_remu: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h3138d0e1;
			endcase
`else
			if (instr_div || instr_divu)
				pcpi_rd <= outsign ? -quotient : quotient;
			else
				pcpi_rd <= outsign ? -dividend : dividend;
`endif
		end else begin
			if (divisor <= dividend) begin
				dividend <= dividend - divisor;
				quotient <= quotient | quotient_msk;
			end
			divisor <= divisor >> 1;
`ifdef RISCV_FORMAL_ALTOPS
			quotient_msk <= quotient_msk >> 5;
`else
			quotient_msk <= quotient_msk >> 1;
`endif
		end
	end
endmodule


Secret-residue fixture (operand registers cleared on completion). Reserved for the `secret_residue` task.

// WS-7 — no-secret-residue repair of picorv32_pcpi_div. Byte-identical to rtl_ct/pcpi_div.v EXCEPT that on the
// completion cycle (`!quotient_msk && running`) the operand-derived scratch registers are ZEROED, so no operand
// residue survives past `done`. pcpi_rd is a non-blocking assignment from the PRE-clock quotient/dividend, so
// the declared result is still latched correctly even though those registers are wiped in the same cycle.
// This is the "templated countermeasure" of the ct-repair pattern (fixed data-oblivious wipe), not general CEGIS.
// Expected: residue_check PROVES this variant (stock pcpi_div is REFUSED).
module picorv32_pcpi_div (
	input clk, resetn,

	input             pcpi_valid,
	input      [31:0] pcpi_insn,
	input      [31:0] pcpi_rs1,
	input      [31:0] pcpi_rs2,
	output reg        pcpi_wr,
	output reg [31:0] pcpi_rd,
	output reg        pcpi_wait,
	output reg        pcpi_ready
);
	reg instr_div, instr_divu, instr_rem, instr_remu;
	wire instr_any_div_rem = |{instr_div, instr_divu, instr_rem, instr_remu};

	reg pcpi_wait_q;
	wire start = pcpi_wait && !pcpi_wait_q;

	always @(posedge clk) begin
		instr_div <= 0;
		instr_divu <= 0;
		instr_rem <= 0;
		instr_remu <= 0;

		if (resetn && pcpi_valid && !pcpi_ready && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin
			case (pcpi_insn[14:12])
				3'b100: instr_div <= 1;
				3'b101: instr_divu <= 1;
				3'b110: instr_rem <= 1;
				3'b111: instr_remu <= 1;
			endcase
		end

		pcpi_wait <= instr_any_div_rem && resetn;
		pcpi_wait_q <= pcpi_wait && resetn;
	end

	reg [31:0] dividend;
	reg [62:0] divisor;
	reg [31:0] quotient;
	reg [31:0] quotient_msk;
	reg running;
	reg outsign;

	always @(posedge clk) begin
		pcpi_ready <= 0;
		pcpi_wr <= 0;
		pcpi_rd <= 'bx;

		if (!resetn) begin
			running <= 0;
		end else
		if (start) begin
			running <= 1;
			dividend <= (instr_div || instr_rem) && pcpi_rs1[31] ? -pcpi_rs1 : pcpi_rs1;
			divisor <= ((instr_div || instr_rem) && pcpi_rs2[31] ? -pcpi_rs2 : pcpi_rs2) << 31;
			outsign <= (instr_div && (pcpi_rs1[31] != pcpi_rs2[31]) && |pcpi_rs2) || (instr_rem && pcpi_rs1[31]);
			quotient <= 0;
			quotient_msk <= 1 << 31;
		end else
		if (!quotient_msk && running) begin
			running <= 0;
			pcpi_ready <= 1;
			pcpi_wr <= 1;
			if (instr_div || instr_divu)
				pcpi_rd <= outsign ? -quotient : quotient;
			else
				pcpi_rd <= outsign ? -dividend : dividend;
			// WS-7 wipe: clear every operand-derived scratch register so no secret residue survives completion.
			dividend <= 0;
			divisor <= 0;
			quotient <= 0;
			outsign <= 0;
		end else begin
			if (divisor <= dividend) begin
				dividend <= dividend - divisor;
				quotient <= quotient | quotient_msk;
			end
			divisor <= divisor >> 1;
			quotient_msk <= quotient_msk >> 1;
		end
	end
endmodule


Secret-residue fixture (only some operand registers cleared) — the discriminating twin. Reserved for the `secret_residue` task.

// WS-7 TEETH — a DELIBERATELY INCOMPLETE wipe. Identical to pcpi_div_wiped.v except it clears only `dividend`
// and leaves `divisor` holding operand-derived residue at completion. residue_check MUST still REFUSE this
// variant: a partial wipe is not no-residue. If this were accepted, the property would be vacuous.
module picorv32_pcpi_div (
	input clk, resetn,

	input             pcpi_valid,
	input      [31:0] pcpi_insn,
	input      [31:0] pcpi_rs1,
	input      [31:0] pcpi_rs2,
	output reg        pcpi_wr,
	output reg [31:0] pcpi_rd,
	output reg        pcpi_wait,
	output reg        pcpi_ready
);
	reg instr_div, instr_divu, instr_rem, instr_remu;
	wire instr_any_div_rem = |{instr_div, instr_divu, instr_rem, instr_remu};

	reg pcpi_wait_q;
	wire start = pcpi_wait && !pcpi_wait_q;

	always @(posedge clk) begin
		instr_div <= 0;
		instr_divu <= 0;
		instr_rem <= 0;
		instr_remu <= 0;

		if (resetn && pcpi_valid && !pcpi_ready && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin
			case (pcpi_insn[14:12])
				3'b100: instr_div <= 1;
				3'b101: instr_divu <= 1;
				3'b110: instr_rem <= 1;
				3'b111: instr_remu <= 1;
			endcase
		end

		pcpi_wait <= instr_any_div_rem && resetn;
		pcpi_wait_q <= pcpi_wait && resetn;
	end

	reg [31:0] dividend;
	reg [62:0] divisor;
	reg [31:0] quotient;
	reg [31:0] quotient_msk;
	reg running;
	reg outsign;

	always @(posedge clk) begin
		pcpi_ready <= 0;
		pcpi_wr <= 0;
		pcpi_rd <= 'bx;

		if (!resetn) begin
			running <= 0;
		end else
		if (start) begin
			running <= 1;
			dividend <= (instr_div || instr_rem) && pcpi_rs1[31] ? -pcpi_rs1 : pcpi_rs1;
			divisor <= ((instr_div || instr_rem) && pcpi_rs2[31] ? -pcpi_rs2 : pcpi_rs2) << 31;
			outsign <= (instr_div && (pcpi_rs1[31] != pcpi_rs2[31]) && |pcpi_rs2) || (instr_rem && pcpi_rs1[31]);
			quotient <= 0;
			quotient_msk <= 1 << 31;
		end else
		if (!quotient_msk && running) begin
			running <= 0;
			pcpi_ready <= 1;
			pcpi_wr <= 1;
			if (instr_div || instr_divu)
				pcpi_rd <= outsign ? -quotient : quotient;
			else
				pcpi_rd <= outsign ? -dividend : dividend;
			// INCOMPLETE wipe (teeth): dividend cleared, but divisor/quotient/outsign residue survives.
			dividend <= 0;
		end else begin
			if (divisor <= dividend) begin
				dividend <= dividend - divisor;
				quotient <= quotient | quotient_msk;
			end
			divisor <= divisor >> 1;
			quotient_msk <= quotient_msk >> 1;
		end
	end
endmodule


Secret-residue fixture. Reserved for the `secret_residue` task.

// Vendored standalone copy of picorv32_pcpi_mul (the RISC-V MUL/MULH co-processor) from
// the upstream picorv32 project (ISC license) -- see LICENSE-FIXTURES for full attribution.
// Completion is `pcpi_ready <= mul_finish`, and `mul_finish <= mul_counter[6]` where `mul_counter`
// is loaded from a fixed constant (63 or 31, selected by the *instruction* `instr_any_mulh`, never by
// the operands) and decremented by STEPS_AT_ONCE each cycle. The completion cycle therefore depends only
// on the public opcode, not on the secret operands `pcpi_rs1`/`pcpi_rs2` — i.e. the shift-add multiply is
// constant-time. Used as an additional real-core constant-time substrate for the
// completion-cone analysis. Not modified (parameter defaults are picorv32's own).
module picorv32_pcpi_mul #(
	parameter STEPS_AT_ONCE = 1,
	parameter CARRY_CHAIN = 4
) (
	input clk, resetn,

	input             pcpi_valid,
	input      [31:0] pcpi_insn,
	input      [31:0] pcpi_rs1,
	input      [31:0] pcpi_rs2,
	output reg        pcpi_wr,
	output reg [31:0] pcpi_rd,
	output reg        pcpi_wait,
	output reg        pcpi_ready
);
	reg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu;
	wire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu};
	wire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu};
	wire instr_rs1_signed = |{instr_mulh, instr_mulhsu};
	wire instr_rs2_signed = |{instr_mulh};

	reg pcpi_wait_q;
	wire mul_start = pcpi_wait && !pcpi_wait_q;

	always @(posedge clk) begin
		instr_mul <= 0;
		instr_mulh <= 0;
		instr_mulhsu <= 0;
		instr_mulhu <= 0;

		if (resetn && pcpi_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin
			case (pcpi_insn[14:12])
				3'b000: instr_mul <= 1;
				3'b001: instr_mulh <= 1;
				3'b010: instr_mulhsu <= 1;
				3'b011: instr_mulhu <= 1;
			endcase
		end

		pcpi_wait <= instr_any_mul;
		pcpi_wait_q <= pcpi_wait;
	end

	reg [63:0] rs1, rs2, rd, rdx;
	reg [63:0] next_rs1, next_rs2, this_rs2;
	reg [63:0] next_rd, next_rdx, next_rdt;
	reg [6:0] mul_counter;
	reg mul_waiting;
	reg mul_finish;
	integer i, j;

	// carry save accumulator
	always @* begin
		next_rd = rd;
		next_rdx = rdx;
		next_rs1 = rs1;
		next_rs2 = rs2;

		for (i = 0; i < STEPS_AT_ONCE; i=i+1) begin
			this_rs2 = next_rs1[0] ? next_rs2 : 0;
			if (CARRY_CHAIN == 0) begin
				next_rdt = next_rd ^ next_rdx ^ this_rs2;
				next_rdx = ((next_rd & next_rdx) | (next_rd & this_rs2) | (next_rdx & this_rs2)) << 1;
				next_rd = next_rdt;
			end else begin
				next_rdt = 0;
				for (j = 0; j < 64; j = j + CARRY_CHAIN)
					{next_rdt[j+CARRY_CHAIN-1], next_rd[j +: CARRY_CHAIN]} =
							next_rd[j +: CARRY_CHAIN] + next_rdx[j +: CARRY_CHAIN] + this_rs2[j +: CARRY_CHAIN];
				next_rdx = next_rdt << 1;
			end
			next_rs1 = next_rs1 >> 1;
			next_rs2 = next_rs2 << 1;
		end
	end

	always @(posedge clk) begin
		mul_finish <= 0;
		if (!resetn) begin
			mul_waiting <= 1;
		end else
		if (mul_waiting) begin
			if (instr_rs1_signed)
				rs1 <= $signed(pcpi_rs1);
			else
				rs1 <= $unsigned(pcpi_rs1);

			if (instr_rs2_signed)
				rs2 <= $signed(pcpi_rs2);
			else
				rs2 <= $unsigned(pcpi_rs2);

			rd <= 0;
			rdx <= 0;
			mul_counter <= (instr_any_mulh ? 63 - STEPS_AT_ONCE : 31 - STEPS_AT_ONCE);
			mul_waiting <= !mul_start;
		end else begin
			rd <= next_rd;
			rdx <= next_rdx;
			rs1 <= next_rs1;
			rs2 <= next_rs2;

			mul_counter <= mul_counter - STEPS_AT_ONCE;
			if (mul_counter[6]) begin
				mul_finish <= 1;
				mul_waiting <= 1;
			end
		end
	end

	always @(posedge clk) begin
		pcpi_wr <= 0;
		pcpi_ready <= 0;
		if (mul_finish && resetn) begin
			pcpi_wr <= 1;
			pcpi_ready <= 1;
			pcpi_rd <= instr_any_mulh ? rd >> 32 : rd;
		end
	end
endmodule


Simulation testbench, not a design under test.

// Cross-check testbench: does the ACTUAL RTL behave as the hand-authored z3 relation claims?
//   - euclid_gcd: two secret operand pairs must complete at DIFFERENT cycles (the leak is real in RTL).
//   - ct_gcd:     any secret operand pair must complete at the SAME fixed cycle (constant-time in RTL).
// Prints "DONE <name> <a> <b> <cycle>" lines; a harness (self_compose cross-check) parses them.
`timescale 1ns/1ps
module tb_ct;
    reg clk = 0; always #5 clk = ~clk;

    // ---- euclid_gcd instance ----
    reg e_rst, e_start; reg [7:0] e_a, e_b;
    wire e_done; wire [7:0] e_g;
    euclid_gcd EU (.clk(clk), .rst(e_rst), .start(e_start), .a_in(e_a), .b_in(e_b),
                   .done(e_done), .gcd_out(e_g));

    // ---- ct_gcd instance ----
    reg c_rst, c_start; reg [7:0] c_a, c_b;
    wire c_done; wire [7:0] c_g;
    ct_gcd CT (.clk(clk), .rst(c_rst), .start(c_start), .a_in(c_a), .b_in(c_b),
               .done(c_done), .gcd_out(c_g));

    integer cyc;

    // run one design to completion; returns cycles from start to done via $display
    task run_euclid(input [7:0] a, input [7:0] b);
        begin
            @(negedge clk); e_rst=1; e_start=0; e_a=0; e_b=0;
            @(negedge clk); e_rst=0; e_start=1; e_a=a; e_b=b;
            @(negedge clk); e_start=0; cyc=1;
            while (!e_done && cyc < 300) begin @(negedge clk); cyc=cyc+1; end
            $display("DONE euclid_gcd %0d %0d %0d", a, b, cyc);
        end
    endtask

    task run_ct(input [7:0] a, input [7:0] b);
        begin
            @(negedge clk); c_rst=1; c_start=0; c_a=0; c_b=0;
            @(negedge clk); c_rst=0; c_start=1; c_a=a; c_b=b;
            @(negedge clk); c_start=0; cyc=1;
            while (!c_done && cyc < 300) begin @(negedge clk); cyc=cyc+1; end
            $display("DONE ct_gcd %0d %0d %0d", a, b, cyc);
        end
    endtask

    initial begin
        run_euclid(8'd66, 8'd108);   // z3 counterexample pair A
        run_euclid(8'd17, 8'd170);   // z3 counterexample pair B  (must differ from A's cycle)
        run_ct(8'd66, 8'd108);       // constant-time: fixed cycle
        run_ct(8'd17, 8'd170);       // constant-time: same fixed cycle
        run_ct(8'd200, 8'd3);        // constant-time: still the same fixed cycle
        $finish;
    end
endmodule