vestigia

Detect where execution depends on secrets.
git clone git://git.ppad.tech/vestigia.git
Log | Files | Refs | README | LICENSE

harness.c (3153B)


      1 /*
      2  * harness.c: the harness the flake's probe check runs under both
      3  * instruments.
      4  *
      5  * Copyright (c) 2026 Jared Tobin
      6  * License: MIT
      7  *
      8  * Its secret, the first argument in hex (of one width for every
      9  * secret, so that runs lay out alike), reaches a loop's bound, a
     10  * table index, which of two calls a helper returns to, a dividend,
     11  * a divisor, a floating-point division (outside either ISA's
     12  * data-independent-timing contract), and, on a path the third secret
     13  * alone takes, another table index; a constant-time select reaches
     14  * none of them.
     15  */
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 
     19 #include "vestigia.h"
     20 
     21 static unsigned char table[256];
     22 static volatile unsigned modulus = 3329;
     23 
     24 __attribute__((noinline)) unsigned branchy(unsigned s) {
     25   unsigned r = 0;
     26   for (unsigned i = 0; i < (s & 7); i++) r += i;
     27   return r;
     28 }
     29 
     30 __attribute__((noinline)) unsigned lookup(unsigned s) {
     31   return table[s & 0xff];
     32 }
     33 
     34 __attribute__((noinline)) unsigned leaf(unsigned x) {
     35   return x * 3u + 1u;
     36 }
     37 
     38 /* leaf runs eight times whatever the secret, but returns to one call
     39    or the other as the secret's bits say */
     40 __attribute__((noinline)) unsigned calls(unsigned s) {
     41   unsigned r = 0;
     42   for (unsigned i = 0; i < 8; i++) {
     43     if ((s >> i) & 1u) r += leaf(i);
     44     else r ^= leaf(i + 1u);
     45   }
     46   return r;
     47 }
     48 
     49 /* a secret divided by a public modulus, the divisor in a register */
     50 __attribute__((noinline)) unsigned divide(unsigned s) {
     51 #if defined(__x86_64__)
     52   unsigned q, r;
     53   __asm__ volatile("divl %4"
     54                    : "=a"(q), "=d"(r)
     55                    : "a"(s * 2654435761u), "d"(0u), "r"(modulus));
     56   return q ^ r;
     57 #else
     58   return (s * 2654435761u) / modulus;
     59 #endif
     60 }
     61 
     62 /* a public value divided by a secret, which x86_64 reads from memory
     63    as the division's operand */
     64 __attribute__((noinline)) unsigned divide_memory(unsigned s) {
     65   unsigned d = (s & 0xffu) | 1u;
     66 #if defined(__x86_64__)
     67   unsigned q, r;
     68   __asm__ volatile("divl %4"
     69                    : "=a"(q), "=d"(r)
     70                    : "a"(1000003u), "d"(0u), "m"(d));
     71   return q ^ r;
     72 #else
     73   return 1000003u / d;
     74 #endif
     75 }
     76 
     77 /* a secret divided in floating point, which neither Arm's DIT nor
     78    Intel's DOITM covers */
     79 __attribute__((noinline)) unsigned fpdivide(unsigned s) {
     80   return (unsigned)((double)s / 7.0);
     81 }
     82 
     83 /* a table index on a path one secret takes */
     84 __attribute__((noinline)) unsigned rare(unsigned s) {
     85   if ((s & 0xffu) == 0xc3u) return table[(s >> 1) & 0xffu];
     86   return 0;
     87 }
     88 
     89 __attribute__((noinline)) unsigned ct(unsigned s, unsigned p) {
     90   unsigned m = 0u - (p & 1u);
     91   return (s & m) | (~s & ~m);
     92 }
     93 
     94 int main(int argc, char **argv) {
     95   unsigned s = (unsigned)strtoul(argc > 1 ? argv[1] : "0", NULL, 16);
     96   unsigned out[8];
     97   for (int i = 0; i < 256; i++) table[i] = (unsigned char)i;
     98   vestigia_secret(&s, sizeof s);
     99   out[0] = branchy(s);
    100   out[1] = lookup(s);
    101   out[2] = calls(s);
    102   out[3] = divide(s);
    103   out[4] = divide_memory(s);
    104   out[5] = rare(s);
    105   out[6] = ct(s, 1);
    106   out[7] = fpdivide(s);
    107   vestigia_public(out, sizeof out);
    108   printf("%u %u %u %u %u %u %u %u\n", out[0], out[1], out[2], out[3],
    109          out[4], out[5], out[6], out[7]);
    110   return 0;
    111 }