Implement 40250 classic client port
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user