File size: 4,704 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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #include "l5_gateway.h"
#include <string.h>
#include "cs_sha256.h"
cs_status gw_request_init(gw_request *r, uint64_t id, uint32_t kind, const void *payload, size_t len)
{
if (!r || (!payload && len) || kind > 31) return CS_ERR_ARG;
r->id = id;
r->kind = kind;
r->payload = payload;
r->len = len;
cs_sha256_ctx c;
cs_sha256_init(&c);
cs_sha256_update(&c, "cstack-req-v1", 13);
cs_sha256_update_u64(&c, id);
cs_sha256_update_u64(&c, kind);
cs_sha256_update_u64(&c, len);
if (len) cs_sha256_update(&c, payload, len);
cs_sha256_final(&c, r->digest);
return CS_OK;
}
static void derive(const uint8_t root[32], const char *label, uint8_t out[32])
{
cs_hmac_sha256(root, 32, label, strlen(label), out);
}
cs_status gw_init(gateway *g, const uint8_t root_key[32], const gw_policy *policy)
{
if (!g || !root_key || !policy) return CS_ERR_ARG;
derive(root_key, "cstack-gw-guardian", g->k_guardian);
derive(root_key, "cstack-gw-examiner", g->k_examiner);
derive(root_key, "cstack-gw-publisher", g->k_publisher);
g->policy = *policy;
g->next_seq = 1;
return CS_OK;
}
static void mac2(const uint8_t key[32], const void *a, size_t alen, const void *b, size_t blen,
uint8_t out[32])
{
uint8_t buf[128];
memcpy(buf, a, alen);
memcpy(buf + alen, b, blen);
cs_hmac_sha256(key, 32, buf, alen + blen, out);
}
static void guardian_mac(const gateway *g, const gw_request *r, uint8_t out[32])
{
mac2(g->k_guardian, "G", 1, r->digest, GW_DIGEST_LEN, out);
}
static void examiner_mac(const gateway *g, const gw_request *r, const gw_token *gt, uint8_t out[32])
{
uint8_t in[GW_DIGEST_LEN * 2];
memcpy(in, r->digest, GW_DIGEST_LEN);
memcpy(in + GW_DIGEST_LEN, gt->mac, GW_DIGEST_LEN);
mac2(g->k_examiner, "E", 1, in, sizeof in, out);
}
static void publisher_mac(const gateway *g, const uint8_t digest[32], uint64_t seq, uint8_t out[32])
{
uint8_t in[GW_DIGEST_LEN + 8];
memcpy(in, digest, GW_DIGEST_LEN);
for (int i = 0; i < 8; i++) in[GW_DIGEST_LEN + i] = (uint8_t)(seq >> (56 - 8 * i));
mac2(g->k_publisher, "P", 1, in, sizeof in, out);
}
cs_status gw_guardian(const gateway *g, const gw_request *r, const gw_health *h, gw_token *out)
{
if (!g || !r || !h || !out) return CS_ERR_ARG;
if (!h->boot_ok || !h->alp_ok || !h->sigma_ok || !h->dissonance_clear) return CS_ERR_DENIED;
guardian_mac(g, r, out->mac);
return CS_OK;
}
cs_status gw_examiner(const gateway *g, const gw_request *r, const gw_token *guardian, gw_token *out)
{
if (!g || !r || !guardian || !out) return CS_ERR_ARG;
uint8_t expect[32];
guardian_mac(g, r, expect);
if (!cs_ct_equal(expect, guardian->mac, 32)) return CS_ERR_DENIED; /* did not pass the Guardian */
/* independent re-derivation: the payload must still match the digest it was bound to */
gw_request fresh;
if (gw_request_init(&fresh, r->id, r->kind, r->payload, r->len) != CS_OK) return CS_ERR_DENIED;
if (!cs_ct_equal(fresh.digest, r->digest, 32)) return CS_ERR_DENIED;
if (!(g->policy.allowed_kinds & (1u << r->kind))) return CS_ERR_DENIED;
if (r->len > g->policy.max_payload) return CS_ERR_DENIED;
examiner_mac(g, r, guardian, out->mac);
return CS_OK;
}
cs_status gw_publisher(gateway *g, const gw_request *r, const gw_token *guardian,
const gw_token *examiner, gw_approval *out)
{
if (!g || !r || !guardian || !examiner || !out) return CS_ERR_ARG;
uint8_t expect[32];
guardian_mac(g, r, expect);
if (!cs_ct_equal(expect, guardian->mac, 32)) return CS_ERR_DENIED;
examiner_mac(g, r, guardian, expect);
if (!cs_ct_equal(expect, examiner->mac, 32)) return CS_ERR_DENIED; /* did not pass the Examiner */
if (g->next_seq == UINT64_MAX) return CS_ERR_STATE;
memcpy(out->request_digest, r->digest, GW_DIGEST_LEN);
out->seq = g->next_seq++;
publisher_mac(g, out->request_digest, out->seq, out->mac);
return CS_OK;
}
cs_status gw_approve(gateway *g, const gw_request *r, const gw_health *h, gw_approval *out)
{
gw_token gt, et;
cs_status s = gw_guardian(g, r, h, >);
if (s != CS_OK) return s;
s = gw_examiner(g, r, >, &et);
if (s != CS_OK) return s;
return gw_publisher(g, r, >, &et, out);
}
cs_status gw_verify_approval(const gateway *g, const gw_request *r, const gw_approval *a)
{
if (!g || !r || !a) return CS_ERR_ARG;
if (!cs_ct_equal(a->request_digest, r->digest, GW_DIGEST_LEN)) return CS_ERR_DENIED;
uint8_t expect[32];
publisher_mac(g, a->request_digest, a->seq, expect);
return cs_ct_equal(expect, a->mac, 32) ? CS_OK : CS_ERR_DENIED;
}
|