Files
utun2/tests/test_group_policy.c
T

119 lines
8.0 KiB
C

/* Реальные подписи, SQLite, Merkle страницы и одна реализация расчёта допуска. */
#include "../src/chat/member_sync.c"
#include <assert.h>
static uint8_t group_x[32] = {81}, group_private[32] = {82}, group_public[32];
struct person { uint64_t id; uint8_t x[32], ed[32], private[32], join[64], tree[64], role[64]; };
static struct person people[8];
static void public_key(const uint8_t private[32], uint8_t public[32]) {
EVP_PKEY* key = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, private, 32); assert(key);
size_t size = 32; assert(EVP_PKEY_get_raw_public_key(key, public, &size) == 1); EVP_PKEY_free(key);
}
static void setup(struct UTUN_INSTANCE* inst, struct UASYNC* ua, int owner) {
memset(inst, 0, sizeof(*inst)); inst->ua = ua; inst->node_id = people[owner ? 0 : 1].id;
assert(sqlite3_open(":memory:", &inst->topo_sqlite_db) == SQLITE_OK);
assert(!topo_node_sqlite_init(inst->topo_sqlite_db));
assert(!topo_node_sqlite_channel_put(inst->topo_sqlite_db, "1001", "policy", people[0].id,
group_x, NULL, group_public, owner ? group_private : NULL, (uint8_t[64]){0}));
assert(!member_sync_init(inst));
}
static void put_person(struct UTUN_INSTANCE* inst, unsigned i, unsigned parent) {
struct person* p = &people[i];
const char* role = i == 1 ? "{\"ver\":\"1\",\"admin\":\"e1\"}" : NULL;
uint8_t body[128];
if (role) {
int n = member_sync_build_owner_msg(p->id, role, body, sizeof(body)); assert(n > 0);
assert(sc_ed25519_sign(group_private, body, n, p->role) == SC_OK);
}
assert(member_sync_sign_join(i ? people[parent].private : group_private, p->join, p->tree) == 0);
assert(member_sync_put(inst, "1001", p->id, p->x, p->ed, p->join, 1, NULL, 0, "{\"name\":\"member\"}",
role, role ? p->role : NULL, 0, i ? people[parent].id : 0, p->tree) >= 0);
}
static void token(struct UTUN_INSTANCE* inst, char out[65]) {
uint8_t hash[32]; assert(!merkle_sync_read_hash(inst, MT_DATA_MEMBERS, "1001", 0, 0, hash));
for (int i = 0; i < 32; i++) snprintf(out + i * 2, 3, "%02x", hash[i]);
}
static int admitted(struct UTUN_INSTANCE* inst, unsigned person) {
return topo_node_sqlite_member_allowed(inst->topo_sqlite_db, "1001", people[person].id);
}
static void page(struct UTUN_INSTANCE* src, struct UTUN_INSTANCE* dst, uint64_t prefix) {
uint8_t data[8192]; size_t size = sizeof(data); int more; merkle_key_t after = {{0}}, next;
assert(!_member_get_page(src, "1001", prefix, 0, &after, data, &size, &next, &more) && !more);
assert(!_member_apply_items(dst, "1001", src->node_id, data, size));
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
struct UASYNC* ua = uasync_create(); assert(ua); public_key(group_private, group_public);
for (unsigned i = 0; i < 8; i++) {
struct person* p = &people[i]; p->x[0] = i + 1; p->private[0] = i + 11;
public_key(p->private, p->ed); p->id = sc_derive_node_id_from_pubkey(p->x);
uint8_t body[112]; assert(member_sync_build_join_msg(group_x, group_public, p->id, p->x, 1, body, sizeof(body)) == 112);
assert(sc_ed25519_sign(p->private, body, sizeof(body), p->join) == SC_OK);
}
struct UTUN_INSTANCE source, target; setup(&source, ua, 1); setup(&target, ua, 0);
const unsigned parents[] = {0,0,0,2,3,1};
for (unsigned i = 0; i < 6; i++) { put_person(&source, i, parents[i]); put_person(&target, i, parents[i]); }
assert(admitted(&source, 0) && admitted(&source, 1) && admitted(&source, 2) && admitted(&source, 5));
assert(!admitted(&source, 3) && !admitted(&source, 4));
assert(group_policy_can_invite(&source, "1001", people[0].id) == 1);
assert(group_policy_can_invite(&source, "1001", people[1].id) == 1);
assert(group_policy_can_invite(&source, "1001", people[2].id) == 0);
char baseline[65], current[65], *json = NULL; token(&source, baseline);
assert(!group_policy_command(&source, "1001", 1, baseline, NULL, &json)); u_free(json);
assert(admitted(&source, 3) && admitted(&source, 4) && group_policy_can_invite(&source, "1001", people[2].id) == 1);
page(&source, &target, 0); member_policy_result(&target, "1001", source.node_id, MT_OK);
assert(admitted(&target, 3) && admitted(&target, 4));
assert(!group_policy_command(&source, "1001", 0, NULL, NULL, &json));
assert(strstr(json, "\"affected\":[{") && strstr(json, "\"can_edit\":true")); u_free(json);
token(&source, baseline);
assert(group_policy_command(&target, "1001", 0, baseline, NULL, &json) < 0 && strstr(json, "forbidden")); u_free(json);
assert(group_policy_command(&source, "1001", 0, "stale", NULL, &json) == -2); u_free(json);
assert(group_policy_command(&source, "1001", 0, baseline, "{}", &json) < 0); u_free(json);
char keep[64]; snprintf(keep, sizeof(keep), "[\"%llu\"]", (unsigned long long)people[3].id);
assert(sqlite3_exec(source.topo_sqlite_db, "CREATE TRIGGER reject_policy BEFORE UPDATE ON group_policy "
"BEGIN SELECT RAISE(ABORT,'rollback test'); END", NULL, NULL, NULL) == SQLITE_OK);
assert(group_policy_command(&source, "1001", 0, baseline, keep, &json) < 0); u_free(json);
token(&source, current); assert(!strcmp(current, baseline) && admitted(&source, 4));
uint8_t signature[64]; uint64_t signer;
assert(!topo_node_sqlite_member_get_sign(source.topo_sqlite_db, "1001", people[3].id, &signer, signature));
assert(signer == people[2].id);
assert(sqlite3_exec(source.topo_sqlite_db, "DROP TRIGGER reject_policy", NULL, NULL, NULL) == SQLITE_OK);
assert(!group_policy_command(&source, "1001", 0, baseline, keep, &json)); u_free(json);
assert(admitted(&source, 3) && !admitted(&source, 4));
assert(!topo_node_sqlite_member_get_sign(source.topo_sqlite_db, "1001", people[3].id, &signer, signature) && !signer);
/* Политика раньше переподписи: текущий допуск сохраняется до полной контрольной точки. */
page(&source, &target, 0); assert(admitted(&target, 3) && admitted(&target, 4));
member_sync_destroy(&target); assert(!member_sync_init(&target));
assert(admitted(&target, 3) && admitted(&target, 4) && group_policy_pending(target.topo_sqlite_db, "1001") == 1);
const uint64_t mask = ~UINT64_C(0) << (64 - MT_MAX_LEVEL * 5);
for (unsigned i = 0; i < 6; i++) page(&source, &target, people[i].id & mask);
assert(admitted(&target, 3) && admitted(&target, 4));
assert(!member_finish(&target, "1001")); member_policy_result(&target, "1001", source.node_id, MT_OK);
assert(admitted(&target, 3) && !admitted(&target, 4));
token(&source, baseline); token(&target, current); assert(!strcmp(baseline, current));
/* Поздняя старая подпись не вытесняет подпись группы той же эпохи. */
put_person(&target, 3, 2); token(&target, current); assert(!strcmp(baseline, current));
member_sync_destroy(&target); assert(!member_sync_init(&target));
assert(admitted(&target, 3) && !admitted(&target, 4));
/* Другое join_sig не может использовать подтверждение прежнего JOIN. */
struct ms_member_rec forged = {.node_id = people[3].id, .x25519 = people[3].x, .join_sig = people[4].join,
.signature = signature, .signed_by = 0};
assert(member_sync_verify_signature(source.topo_sqlite_db, "1001", &forged) == 0);
put_person(&source, 6, 7); assert(!admitted(&source, 6) && !member_finish(&source, "1001"));
put_person(&source, 7, 6); assert(!admitted(&source, 6) && !admitted(&source, 7));
token(&source, baseline);
assert(!group_policy_command(&source, "1001", 1, baseline, NULL, &json)); u_free(json);
assert(!admitted(&source, 6) && !admitted(&source, 7));
member_sync_destroy(&source); member_sync_destroy(&target);
assert(sqlite3_close(source.topo_sqlite_db) == SQLITE_OK && sqlite3_close(target.topo_sqlite_db) == SQLITE_OK);
uasync_destroy(ua, 0); puts("PASS: default, roles, cascade, selection, rollback, stale preview, page order, signature precedence, restart");
return 0;
}