File size: 3,265 Bytes
2166d7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "l3_sigma.h"

#include <time.h>

#include "cs_sha256.h"

static uint64_t default_clock(void *ctx)
{
    (void)ctx;
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
}

void sigma_init(sigma_ctx *s, const sigma_limits *lim, sigma_clock_fn clock, void *clock_ctx)
{
    s->lim = *lim;
    s->mem_used = s->mem_peak = s->steps = 0;
    s->clock = clock ? clock : default_clock;
    s->clock_ctx = clock_ctx;
    s->breach = CS_OK;
    s->start_ns = s->clock(s->clock_ctx);
}

static cs_status latch(sigma_ctx *s, cs_status why)
{
    if (s->breach == CS_OK) s->breach = why;
    return s->breach;
}

cs_status sigma_charge_mem(sigma_ctx *s, uint64_t bytes)
{
    if (!s) return CS_ERR_ARG;
    if (s->breach != CS_OK) return s->breach;
    if (bytes > s->lim.max_mem_bytes - (s->mem_used > s->lim.max_mem_bytes ? s->lim.max_mem_bytes : s->mem_used))
        return latch(s, CS_ERR_LIMIT);
    s->mem_used += bytes;
    if (s->mem_used > s->mem_peak) s->mem_peak = s->mem_used;
    return CS_OK;
}

cs_status sigma_release_mem(sigma_ctx *s, uint64_t bytes)
{
    if (!s) return CS_ERR_ARG;
    if (bytes > s->mem_used) return CS_ERR_RANGE;      /* releasing more than was charged */
    s->mem_used -= bytes;
    return CS_OK;
}

cs_status sigma_charge_steps(sigma_ctx *s, uint64_t n)
{
    if (!s) return CS_ERR_ARG;
    if (s->breach != CS_OK) return s->breach;
    if (n > s->lim.max_steps - (s->steps > s->lim.max_steps ? s->lim.max_steps : s->steps))
        return latch(s, CS_ERR_LIMIT);
    s->steps += n;
    return CS_OK;
}

uint64_t sigma_elapsed_ns(const sigma_ctx *s)
{
    uint64_t now = s->clock(s->clock_ctx);
    return now >= s->start_ns ? now - s->start_ns : 0;
}

cs_status sigma_check_time(sigma_ctx *s)
{
    if (!s) return CS_ERR_ARG;
    if (s->breach != CS_OK) return s->breach;
    if (sigma_elapsed_ns(s) > s->lim.max_ns) return latch(s, CS_ERR_LIMIT);
    return CS_OK;
}

cs_status sigma_status(const sigma_ctx *s) { return s ? s->breach : CS_ERR_ARG; }

/* ---- drift ---- */

cs_status sigma_drift_init(sigma_drift *d, int64_t baseline, int64_t tolerance, unsigned shift)
{
    if (!d || tolerance < 0 || shift < 1 || shift > 16) return CS_ERR_ARG;
    if (baseline > INT64_MAX / 4 || baseline < INT64_MIN / 4) return CS_ERR_ARG;
    d->baseline = baseline;
    d->tolerance = tolerance;
    d->ewma = baseline;
    d->shift = shift;
    d->samples = 0;
    d->state = CS_OK;
    return CS_OK;
}

cs_status sigma_drift_update(sigma_drift *d, int64_t sample)
{
    if (!d) return CS_ERR_ARG;
    if (d->state != CS_OK) return d->state;
    if (sample > INT64_MAX / 4 || sample < INT64_MIN / 4) return CS_ERR_ARG;
    d->samples++;
    d->ewma += (sample - d->ewma) / ((int64_t)1 << d->shift);   /* truncates toward zero */
    int64_t dev = d->ewma - d->baseline;
    if (dev < 0) dev = -dev;
    if (dev > d->tolerance) d->state = CS_ERR_DRIFT;
    return d->state;
}

cs_status sigma_state_check(const uint8_t expected[32], const void *state, size_t len)
{
    if (!expected || (!state && len)) return CS_ERR_ARG;
    uint8_t h[32];
    cs_sha256(state ? state : "", len, h);
    return cs_ct_equal(h, expected, 32) ? CS_OK : CS_ERR_DRIFT;
}