Files
mtgodot-poc/extension/tests/net_classic_cipher_test.cpp
T

105 lines
3.9 KiB
C++

// net_classic_cipher_test — 40250 _IMPROVED_PACKET_ENCRYPTION_ end to end,
// no socket: a client-polarity ClassicCipher and a server-polarity one run the
// DH2 key agreement against each other, then encrypt/decrypt must round-trip
// symmetrically (client.encrypt -> server.decrypt and vice versa).
#include "../src/net/classic/classic_cipher.h"
#include "../src/net/classic/wire_classic.h"
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
using namespace mtnet::classic;
static int g_fail = 0;
#define CHECK(c, msg) \
do { \
if (!(c)) { \
std::fprintf(stderr, "FAIL: %s\n", msg); \
++g_fail; \
} \
} while (0)
int main() {
ClassicCipher client; // polarity = true (EterLib/NetStream.cpp:3004)
ClassicCipher server; // polarity = false (desc.cpp FinishHandshake)
// Each side generates its DH2 public blob (spub || epub).
uint8_t cbuf[KEY_AGREEMENT_MAX_DATA_LEN];
uint8_t sbuf[KEY_AGREEMENT_MAX_DATA_LEN];
size_t clen = sizeof(cbuf);
size_t slen = sizeof(sbuf);
size_t cagreed = client.prepare(cbuf, &clen);
size_t sagreed = server.prepare(sbuf, &slen);
CHECK(cagreed != 0, "client prepare -> non-zero agreed length");
CHECK(sagreed != 0, "server prepare -> non-zero agreed length");
CHECK(cagreed == sagreed, "both sides same agreed-value length (same DH2 params)");
CHECK(clen > 0 && clen <= KEY_AGREEMENT_MAX_DATA_LEN, "client blob length sane");
CHECK(clen == slen, "blob lengths match");
// Activate: each consumes the PEER's (agreed_length, blob).
bool ca = client.activate(true, sagreed, sbuf, slen);
bool sa = server.activate(false, cagreed, cbuf, clen);
CHECK(ca, "client activate");
CHECK(sa, "server activate");
CHECK(client.key_ready() && server.key_ready(), "both have encoder+decoder");
// The stream turns real only after GC_KEY_AGREEMENT_COMPLETED.
CHECK(!client.activated(), "cipher not 'activated' until set_activated");
client.set_activated(true);
server.set_activated(true);
// --- round trip: client -> server ---
{
std::string plain = "the quick brown fox CG_MOVE \x07\x01\x00\x00 jumps";
std::vector<uint8_t> buf(plain.begin(), plain.end());
std::vector<uint8_t> orig = buf;
client.encrypt(buf.data(), buf.size());
CHECK(buf != orig, "ciphertext differs from plaintext");
server.decrypt(buf.data(), buf.size());
CHECK(buf == orig, "server recovers client's plaintext");
}
// --- round trip: server -> client, and CTR keystream continuity ---
{
std::string a = "GC_MAIN_CHARACTER hello world 0123456789";
std::string b = "second chunk keeps the CTR counter advancing";
std::vector<uint8_t> ba(a.begin(), a.end()), bb(b.begin(), b.end());
std::vector<uint8_t> oa = ba, ob = bb;
server.encrypt(ba.data(), ba.size());
server.encrypt(bb.data(), bb.size());
client.decrypt(ba.data(), ba.size());
client.decrypt(bb.data(), bb.size());
CHECK(ba == oa && bb == ob, "client recovers server's two chunks (CTR continuity)");
}
// --- a fresh pair must derive a *different* key (ephemeral) ---
{
ClassicCipher c2, s2;
uint8_t x[256], y[256];
size_t xl = sizeof(x), yl = sizeof(y);
size_t xa = c2.prepare(x, &xl);
size_t ya = s2.prepare(y, &yl);
c2.activate(true, ya, y, yl);
s2.activate(false, xa, x, xl);
c2.set_activated(true);
s2.set_activated(true);
std::string msg = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // 32 identical bytes
std::vector<uint8_t> p1(msg.begin(), msg.end());
std::vector<uint8_t> p2 = p1;
client.encrypt(p1.data(), p1.size()); // from the first session (already advanced)
c2.encrypt(p2.data(), p2.size()); // fresh session
CHECK(p1 != p2, "different sessions produce different ciphertext");
}
if (g_fail) {
std::fprintf(stderr, "%d check(s) failed\n", g_fail);
return 1;
}
std::puts("net_classic_cipher_test OK");
return 0;
}