|
|
|
|
@ -25,6 +25,62 @@ static void public_key(const uint8_t priv[32], uint8_t pub[32]) {
|
|
|
|
|
size_t len = 32; assert(EVP_PKEY_get_raw_public_key(pk, pub, &len) == 1); EVP_PKEY_free(pk); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Подтверждение корня доставляется независимо от версии профиля и переживает нулевой блок на проводе. */ |
|
|
|
|
static void test_invitation_certificate(struct UASYNC* ua) { |
|
|
|
|
struct UTUN_INSTANCE source, target; setup(&source, ua); setup(&target, ua); |
|
|
|
|
uint8_t x[32] = {9}, priv[32] = {7}, pub[32], owner_priv[32] = {8}, owner_pub[32], zero[64] = {0}; |
|
|
|
|
public_key(priv, pub); public_key(owner_priv, owner_pub); |
|
|
|
|
struct UTUN_INSTANCE* nodes[] = { &source, &target }; |
|
|
|
|
for (unsigned i = 0; i < 2; i++) { |
|
|
|
|
assert(topo_node_sqlite_channel_delete(nodes[i]->topo_sqlite_db, "1001") == 0); |
|
|
|
|
assert(topo_node_sqlite_channel_put(nodes[i]->topo_sqlite_db, "1001", "root", 1, x, NULL, owner_pub, NULL, zero) == 0); |
|
|
|
|
} |
|
|
|
|
uint64_t nid = sc_derive_node_id_from_pubkey(x); uint8_t join[64], tree[64], update[64], body[256]; |
|
|
|
|
int len = member_sync_build_join_msg(x, owner_pub, nid, x, 1, body, sizeof(body)); assert(len == 112); |
|
|
|
|
assert(sc_ed25519_sign(priv, body, len, join) == SC_OK && member_sync_sign_pubkey(owner_priv, x, tree) == 0); |
|
|
|
|
struct ms_member_rec rec = { .node_id = nid, .x25519 = x, .ed25519 = pub, .join_sig = join, .join_ts = 1, |
|
|
|
|
.update_sig = update, .update_ts = 2, .userinfo = "{\"name\":\"profile\"}", .signature = tree }; |
|
|
|
|
len = member_sync_build_update_msg(join, rec.update_ts, rec.userinfo, body, sizeof(body)); assert(len > 0); |
|
|
|
|
assert(sc_ed25519_sign(priv, body, len, update) == SC_OK); |
|
|
|
|
assert(member_sync_apply_record(&source, "1001", source.node_id, &rec) & MS_APPLY_CHANGED); |
|
|
|
|
rec.signature = zero; |
|
|
|
|
assert(member_sync_apply_record(&target, "1001", target.node_id, &rec) & MS_APPLY_CHANGED); |
|
|
|
|
assert(member_sync_verify_signature(target.topo_sqlite_db, "1001", &rec) == 0); |
|
|
|
|
rec.signature = tree; |
|
|
|
|
assert(member_sync_apply_record(&target, "1001", source.node_id + 1, &rec) & MS_APPLY_CHANGED); |
|
|
|
|
uint8_t expected[32], actual[32], stored[64]; uint64_t parent; |
|
|
|
|
root(&source, expected); root(&target, actual); assert(!memcmp(expected, actual, 32)); |
|
|
|
|
assert(topo_node_sqlite_member_get_sign(target.topo_sqlite_db, "1001", nid, &parent, stored) == 0); |
|
|
|
|
assert(parent == 0 && !memcmp(tree, stored, 64)); |
|
|
|
|
assert(member_sync_apply_record(&target, "1001", source.node_id + 1, &rec) == 0); |
|
|
|
|
tree[0] ^= 1; |
|
|
|
|
assert(member_sync_apply_record(&target, "1001", source.node_id + 1, &rec) < 0); |
|
|
|
|
root(&target, actual); assert(!memcmp(expected, actual, 32)); tree[0] ^= 1; |
|
|
|
|
|
|
|
|
|
/* Более свежий подписанный профиль с нулевой подписью приглашения сохраняет сертификат. */ |
|
|
|
|
rec.update_ts = 3; rec.userinfo = "{\"name\":\"new profile\"}"; |
|
|
|
|
len = member_sync_build_update_msg(join, rec.update_ts, rec.userinfo, body, sizeof(body)); assert(len > 0); |
|
|
|
|
assert(sc_ed25519_sign(priv, body, len, update) == SC_OK); |
|
|
|
|
assert(member_sync_apply_record(&source, "1001", source.node_id, &rec) & MS_APPLY_CHANGED); |
|
|
|
|
rec.signature = zero; |
|
|
|
|
assert(member_sync_apply_record(&target, "1001", source.node_id + 1, &rec) & MS_APPLY_CHANGED); |
|
|
|
|
assert(topo_node_sqlite_member_get_sign(target.topo_sqlite_db, "1001", nid, &parent, stored) == 0); |
|
|
|
|
assert(parent == 0 && !memcmp(tree, stored, 64)); |
|
|
|
|
root(&source, expected); root(&target, actual); assert(!memcmp(expected, actual, 32)); |
|
|
|
|
|
|
|
|
|
/* Старый профиль может донести подтверждение, сохранив более свежие userinfo/update_ts. */ |
|
|
|
|
assert(sqlite3_exec(target.topo_sqlite_db, "UPDATE peers_1001 SET signature=zeroblob(64)", NULL, NULL, NULL) == SQLITE_OK); |
|
|
|
|
assert(merkle_sync_recompute_path(&target, MT_DATA_MEMBERS, "1001", nid) == 1); |
|
|
|
|
rec.update_ts = 2; rec.userinfo = "{\"name\":\"old profile\"}"; rec.signature = tree; |
|
|
|
|
len = member_sync_build_update_msg(join, rec.update_ts, rec.userinfo, body, sizeof(body)); assert(len > 0); |
|
|
|
|
assert(sc_ed25519_sign(priv, body, len, update) == SC_OK); |
|
|
|
|
int result = member_sync_apply_record(&target, "1001", source.node_id + 1, &rec); |
|
|
|
|
assert((result & MS_APPLY_CHANGED) && (result & MS_APPLY_STALE)); |
|
|
|
|
root(&target, actual); assert(!memcmp(expected, actual, 32)); |
|
|
|
|
puts("PASS: root certificate validates with channel key; equal/stale profile versions do not block confirmation; empty wire signature preserves it"); |
|
|
|
|
for (unsigned i = 0; i < 2; i++) { member_sync_destroy(nodes[i]); assert(sqlite3_close(nodes[i]->topo_sqlite_db) == SQLITE_OK); } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_owner_sizes(struct UASYNC* ua) { |
|
|
|
|
struct UTUN_INSTANCE source, target; setup(&source, ua); setup(&target, ua); |
|
|
|
|
uint8_t x[32] = {9}, priv[32] = {7}, pub[32], owner_priv[32] = {8}, owner_pub[32], zero[64] = {0}; |
|
|
|
|
@ -138,6 +194,7 @@ int main(void) {
|
|
|
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); |
|
|
|
|
size_t baseline = u_get_allocated_count(); |
|
|
|
|
struct UASYNC* ua = uasync_create(); assert(ua); |
|
|
|
|
test_invitation_certificate(ua); |
|
|
|
|
test_owner_sizes(ua); |
|
|
|
|
struct UTUN_INSTANCE source, target; |
|
|
|
|
setup(&source, ua); setup(&target, ua); |
|
|
|
|
|