################################################################### Verification methodology ################################################################### Claims in the previous chapters are only as strong as the evidence that the code in ``arcana`` actually behaves the way those chapters describe. This chapter catalogues the **evidence artefacts** the project intends to maintain and how they are produced. The methodology is **shared with quantica**: the ``silentops`` crate provides the verification primitives, and the operator-facing runbook (``doc/infra/ctgrind.md``) is the same on both sides. The content below is the arcana-specific application. Two complementary techniques will be used: * **ctgrind** — Valgrind memcheck client-requests applied to a dedicated test binary. Covers *control-flow* and *memory-access* constant-time properties, run on the host CI. * **silentops::verify** (dudect) — statistical timing test, run on the target hardware. Covers *microarchitectural* timing properties (cache, branch predictor, variable-latency instructions). Neither alone is sufficient; both are required for a credible CT assurance story. A third-party evaluator will expect both classes of evidence in the documentation pack. .. contents:: :local: :depth: 2 Current state (2026-04-21) ========================== * **ctgrind** harness: built for quantica (``quantica_bench/src/bin/ctgrind.rs``). An arcana-side mirror is item ``T3-A``. * **dudect** harness: ``silentops::verify`` is in place; arcana tests have not yet been wired into a per-algorithm harness. Item ``T3-B``. * **Release-asm verification**: ``cargo rustc --release ... --emit=asm`` followed by branch-counting on the curve-arithmetic functions is the **current** form of arcana CT evidence, executed manually on commit ``76191c1``. The expected branch counts are documented in :doc:`countermeasures/ecdsa_ecdh` (table: *Verification artefact*). ctgrind (Valgrind memcheck) — control-flow CT (``T3-A``) ========================================================= Methodology ----------- The technique was first described by Adam Langley in 2010 :cite:`langley2010ctgrind`. A memory buffer holding a secret is marked **undefined** via the Valgrind ``make_mem_undefined`` client request. Any subsequent conditional branch, pointer dereference, or syscall argument that consumes bits of the undefined buffer raises a memcheck error — i.e. any control-flow or addressing decision that depends on the secret is reported. Because memcheck propagates definedness through pure arithmetic, masked bitwise selects (``b ^ (mask & (a ^ b))``) do not raise errors; only the downstream *use* of their result in a branch or load does. A correctly written CT primitive therefore passes ctgrind cleanly; a bug such as a pointer-CMOV → load triggers an error on the load. A 2024 evaluation :cite:`geimer2024_ct_tools_usability` of ctgrind, dudect, MicroWalk and ct-verif concludes that **ctgrind has the best effort/discovery ratio** of the four — the integration is trivial (one client request per secret buffer; no patches, no custom build) and the false-positive rate is low for properly written code. Implementation route in arcana ------------------------------ Mirror the quantica side: 1. ``silentops::ct_grind::poison`` / ``unpoison`` already exist and emit the Valgrind client requests via inline ``asm!``. Arcana uses these unchanged. 2. New ``arcana_bench`` companion crate (mirror of ``quantica_bench``) with a ``src/bin/ctgrind.rs`` driver. The driver poisons the truly secret portions of each typed key (skipping public components like ``ek``-derived material), runs the full keygen / sign / verify or encrypt / decrypt round-trip per parameter set, then unpoisons before asserting correctness. 3. ``tools/ctgrind.sh`` — already exists workspace-wide; the arcana-side ``arcana`` flavour will be added as a new mode (``tools/ctgrind.sh arcana`` running all primitives; ``tools/ctgrind.sh arcana `` for individual runs). 4. ``tools/ctgrind.supp`` — same suppression file format as quantica; each justified suppression carries an inline threat-model paragraph. Per-primitive scope of the arcana ctgrind run --------------------------------------------- .. list-table:: :header-rows: 1 :widths: 25 50 25 * - Primitive - What is poisoned (= secret) - Expected status (post T1, T2) * - ``cipher::aes::Aes*`` - 16/24/32-byte key (after T1-A: round keys derived from the 8-block bitsliced state) - ``T1-A`` mandatory before this is meaningful * - ``cipher::modes::Gcm`` decrypt - Tag bytes received from the wire - ``ct_eq`` already in place; expect 0 findings * - ``cipher::ccm`` decrypt - Tag bytes - Same; expect 0 findings * - ``cipher::xts::AesXts`` encrypt/decrypt - The key-pair (data key + tweak key) - Inherits ``T1-A`` status * - ``cipher::chacha20poly1305`` decrypt - Tag bytes - ``ct_eq`` already in place; expect 0 findings * - ``mac::ctx::Mac::sign`` / ``verify`` - Key bytes - HMAC-SHA-2 path: 0 control-flow findings (CDPA is power, not control-flow). Post ``T2-D``: same. * - ``rsa::rsa::rsa_decrypt_raw`` (CRT path) - ``RsaSecretKey`` ``BigInt`` storage - **Pre-T1-C, T1-E**: expected findings. **Post**: 0 (or documented suppressions only) * - ``ecc::curves::Curve::sign_rfc6979`` / ``sign_random`` / ``ecdh`` - ``SecretKey.bytes`` - **Post-76191c1, pre-T1-B**: spot run pending; expected 0 on the ladder; may surface findings in ``rfc6979_k`` / ``reduce_mod_n`` * - ``ecc::eddsa::ed25519_sign`` - ``Ed25519SecretKey`` 32 bytes - Pre ``T1-F``: audit pending; spot run will set the baseline * - ``ecc::x25519::x25519_ecdh`` / ``x448::x448_ecdh`` - sk 32-byte / 56-byte arrays - Pre ``T1-G``: audit pending Each primitive is run for every parameter set and the result is recorded in ``doc/analysis/arcana_ctgrind_.md`` (mirror of the quantica ``ctgrind-evolution.md`` artefact). dudect (statistical timing) — microarchitectural CT (``T3-B``) ============================================================== Methodology ----------- The shared ``silentops::verify`` module implements the dudect methodology of Reparaz, Balasch & Verbauwhede :cite:`reparaz2017dudect`. The harness: * Calls the cryptographic function in two classes — *fixed* input and *random* input — interleaved. * Measures CPU cycles per call via ``rdtsc`` (or the equivalent on aarch64 / RISC-V). * Computes a Welch's t-statistic between the two timing distributions. * A ``|t| < 4.5`` after ~10⁶ samples is considered passing (``p < 10⁻⁵``). Per-primitive harnesses planned for arcana ------------------------------------------ * ``scalar_mul_point`` — fixed-vs-random scalar on each of the 7 short-Weierstrass curves. **First priority**: closes the hardening loop opened by commit ``76191c1``. * ``rsa_decrypt_raw`` (CRT path) — fixed-vs-random ciphertext. **Required for** ``T1-C`` (Aumüller) **acceptance**. * AEAD decrypt tag-check on each AEAD mode — fixed-vs-random tag. Regression test on the ``ct_eq`` chain. * Once ``T1-A`` lands: fixsliced AES round function — fixed-vs- random key. Each harness is a separate example under ``arcana_bench/examples/dudect_.rs`` and is wired into the ``tools/`` script set. Microarchitectural caveat ------------------------- dudect detects timing leaks **on the actual hardware** the harness runs on. A pass on x86_64 desktop does not guarantee a pass on Cortex-M0 — the latter has its own pipeline and branch-prediction quirks. A third-party evaluation lab will likely re-run the harness on the target silicon; arcana ships the *harness*, not the *evidence on every target*. Release-asm branch counting (current evidence) ============================================== Until ``T3-A`` and ``T3-B`` land, the project's CT evidence is **release-asm branch counting** on key functions, executed manually with: .. code-block:: bash cargo rustc -p arcana --release --lib -- --emit=asm -C opt-level=3 ASM=target/release/deps/arcana-*.s for sym in $(grep -oE '_ZN6arcana3ecc5curve[0-9]+[a-z_]+17h[0-9a-f]+E' $ASM | sort -u); do n=$(awk "/$sym:/,/^\\.Lfunc_end/" $ASM | grep -cE '\bj(e|ne|z|nz|l|le|g|ge|a|b)\b') echo "$n $sym" done | sort -rn Expected counts post-commit ``76191c1`` (documented in :doc:`countermeasures/ecdsa_ecdh`): * ``scalar_mul_point``: 1 (loop counter only) * ``point_double``: 0 * ``point_add_ct``: inlined, 0 point-value branches * ``point_add`` (verify, public): 6 * ``double_scalar_mul`` (verify, public): 2 A regression on these counts (especially the **0** entries) means LLVM has reintroduced a secret-dependent branch and must be investigated immediately — that is the most direct CT regression indicator we currently have. Forward references ================== * :doc:`primitives` — the silentops side of the verification toolchain (poison/unpoison primitives, dudect surface). * :file:`doc/infra/ctgrind.md` — the operator runbook (shared with quantica, when arcana adopts it). * :doc:`biblio` — references for the verification techniques.