// timing.c: una comparación que sale apenas encuentra una diferencia
// filtra, por el tiempo, cuántos bytes del secreto acertó el atacante.
//   uso: ./timing
#include <stdio.h>
#include <string.h>
#include <time.h>

#define LEN 64
#define REPS 2000000

static const char secret[LEN + 1] =
    "s3cr3t-token-0123456789abcdef0123456789abcdef0123456789abcdef012";

// Insegura: termina en el primer byte distinto
static int naive_eq(const char *a, const char *b) {
    for (int i = 0; i < LEN; i++)
        if (a[i] != b[i])
            return 0;
    return 1;
}

// Tiempo constante: siempre recorre todo y acumula las diferencias
static int ct_eq(const char *a, const char *b) {
    unsigned char diff = 0;
    for (int i = 0; i < LEN; i++)
        diff |= (unsigned char)(a[i] ^ b[i]);
    return diff == 0;
}

static double ns_per_call(int (*eq)(const char *, const char *), const char *guess) {
    struct timespec t0, t1;
    volatile int sink = 0;
    clock_gettime(CLOCK_MONOTONIC, &t0);
    for (int r = 0; r < REPS; r++)
        sink += eq(secret, guess);
    clock_gettime(CLOCK_MONOTONIC, &t1);
    (void)sink;
    double ns = (t1.tv_sec - t0.tv_sec) * 1e9 + (t1.tv_nsec - t0.tv_nsec);
    return ns / REPS;
}

int main(void) {
    printf("aciertos   ingenua(ns)   tiempo-constante(ns)\n");
    int prefixes[] = {0, 16, 32, 48, 63};
    for (int k = 0; k < 5; k++) {
        char guess[LEN + 1];
        memset(guess, '?', LEN);
        guess[LEN] = '\0';
        memcpy(guess, secret, prefixes[k]);   // los primeros k bytes son correctos
        printf("%8d   %11.1f   %20.1f\n", prefixes[k],
               ns_per_call(naive_eq, guess), ns_per_call(ct_eq, guess));
    }
    return 0;
}
