From 5ce650382352c1503f40abf08129ada69402af55 Mon Sep 17 00:00:00 2001 From: bitflash-coin Date: Tue, 4 Aug 2026 06:34:32 -0700 Subject: public commit --- src/test_btfrv.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/test_btfrv.cpp (limited to 'src/test_btfrv.cpp') diff --git a/src/test_btfrv.cpp b/src/test_btfrv.cpp new file mode 100644 index 0000000..291e660 --- /dev/null +++ b/src/test_btfrv.cpp @@ -0,0 +1,116 @@ +// Standalone loopback test of the rendezvous transport + echan on top. +// A relay, a service and a client all run in one process on 127.0.0.1 (a unit +// test of the protocol logic -- NOT a network/LAN test). Proves: service +// registers, client pairs by pubkey through the relay, and they exchange data +// end-to-end encrypted so the relay only ever forwards ciphertext. +// +// Build (UCRT64): +// g++ -std=gnu++14 test_btfrv.cpp btfrv.cpp btfchan.cpp -lsodium -lws2_32 -o test_btfrv.exe + +#include "btfrv.h" +#include "btfchan.h" +#include +#include +#include +#include +#include +#include + +using namespace btf; +static int g_fail = 0; +#define CHECK(cond, name) do { \ + if (cond) { printf(" ok %s\n", name); } \ + else { printf(" FAIL %s\n", name); g_fail++; } } while(0) + +static const unsigned short PORT = 18443; + +// echan framing over a rendezvous socket +static bool WriteFrame(RvSocket s, const unsigned char shared[32], const std::string& pt) +{ + std::vector f = ChanEncryptFrame(shared, (const unsigned char*)pt.data(), pt.size()); + return RvWriteN(s, f.data(), (int)f.size()); +} +static bool ReadFrame(RvSocket s, const unsigned char shared[32], std::string& out) +{ + unsigned char len[4]; + if (!RvReadN(s, len, 4)) return false; + size_t n = ((size_t)len[0]<<24)|((size_t)len[1]<<16)|((size_t)len[2]<<8)|len[3]; + if (n == 0 || n > (1u<<20)) return false; + std::vector body(n); + if (!RvReadN(s, body.data(), (int)n)) return false; + std::vector pt; + if (!ChanDecryptBody(shared, body.data(), (int)n, pt)) return false; + out.assign((char*)pt.data(), pt.size()); + return true; +} + +static std::string g_svcGot; +static bool g_svcOk = false; + +static void ServiceThread(std::vector pairing_id, std::vector svc_enc_sk) +{ + RvSocket s = RvServiceRegister("127.0.0.1", PORT, pairing_id.data()); + if (s == RV_INVALID) return; + // read the client's ephemeral x25519 public key + unsigned char eph[32]; + if (!RvReadN(s, eph, 32)) { RvClose(s); return; } + unsigned char shared[32]; + if (!ChanEndpointHandshake(eph, svc_enc_sk.data(), shared)) { RvClose(s); return; } + std::string msg; + if (!ReadFrame(s, shared, msg)) { RvClose(s); return; } + g_svcGot = msg; + WriteFrame(s, shared, std::string("block accepted: ") + msg); + g_svcOk = true; + RvClose(s); +} + +int main() +{ + if (!ChanInit() || !RvInit()) { printf("init failed\n"); return 2; } + + // service identity: a 32-byte .btf pairing id + an x25519 enc keypair + unsigned char svc_enc_pk[32], svc_enc_sk[32]; + ChanKeypair(svc_enc_pk, svc_enc_sk); + std::vector pairing_id(32); + for (int i = 0; i < 32; i++) pairing_id[i] = (unsigned char)(i * 7 + 1); + + printf("rendezvous_relay_pairs_and_forwards_encrypted\n"); + + std::thread relay([]{ RvRelayRun(PORT); }); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); // let it bind + + std::thread svc(ServiceThread, pairing_id, std::vector(svc_enc_sk, svc_enc_sk+32)); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); // let service register + + // client dials the pairing id through the relay + RvSocket c = RvClientConnect("127.0.0.1", PORT, pairing_id.data()); + CHECK(c != RV_INVALID, "client paired to service via relay"); + if (c != RV_INVALID) + { + unsigned char eph_pub[32], shared_client[32]; + CHECK(ChanClientHandshake(svc_enc_pk, eph_pub, shared_client), "client handshake"); + CHECK(RvWriteN(c, eph_pub, 32), "client sends ephemeral pubkey"); + CHECK(WriteFrame(c, shared_client, "hello one-cpu-one-vote"), "client sends encrypted frame"); + std::string reply; + CHECK(ReadFrame(c, shared_client, reply), "client reads encrypted reply"); + CHECK(reply == "block accepted: hello one-cpu-one-vote", "end-to-end message round-trips through relay"); + RvClose(c); + } + + svc.join(); + CHECK(g_svcOk && g_svcGot == "hello one-cpu-one-vote", "service received the decrypted plaintext"); + + printf("unknown_pubkey_is_rejected\n"); + { + unsigned char other[32]; + for (int i = 0; i < 32; i++) other[i] = 0xEE; + RvSocket bad = RvClientConnect("127.0.0.1", PORT, other); + CHECK(bad == RV_INVALID, "client for unregistered .btf is rejected"); + } + + RvRelayStop(); + relay.join(); + + printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail); + return g_fail == 0 ? 0 : 1; +} -- cgit v1.2.3