This may be the best way to do totally stateless handshakes for VIE encryption. syncookies.txt : -----------------
Executive summary
-----------------
I've developed new, stateless protection against spoofing connections to disrupt an ASSS zone.
This protection is only for SubSpace (not Continuum) clients, as Ctm already has this ability.
All SS clients should be compatible. Â The following are tested: MERVBot (build 41), VIE SubSpace 1.34
New protection is only used when module enc_vie is loaded.
Cons:
24 additional bytes overhead on SS/bot connections
Slightly higher rate of connection failure due to ordinary packetloss; bots won't notice this.
Pros:
VIE connections cannot be spoofed, which disables a large number of attack vectors.
----------------------------------------------------
VIE SubSpace 1.34 connecting to ASSS with syncookies
----------------------------------------------------
c2s    Connection from 127.0.0.1:1817 (proto 1)
c2s 0000 Â 00 01 09 7F 9D E6 01 00 Â Â Â Â Â Â Â Â Â Â Â Â Â ........
Nothing new, yet.
s2c 0000 Â 00 0E 06 00 05 C7 B2 5A 1D 06 00 05 C8 6D 38 E7 Â .......Z.....m8.
s2c 0010 Â 06 00 05 F7 80 62 19 06 00 02 F7 80 62 19 Â Â Â Â .....b......b.
Pow! Â That doesn't look normal!
The server hasn't actually connected with the client yet, despite that 00 02.
To break it down:
00 0E
-- clustered to reduce chance of packetloss and reordering
   06  00 05 C7 B2 5A 1D
-- syncookie, sent first so that it's returned first and processed first
   06  00 05 C8 6D 38 E7
-- used to bust through encrypted responses, sent second to reduce calculations, MSB=0
   06  00 05 F7 80 62 19
-- same as the key, sent third because the second one had more reason to be second
   06  00 02 F7 80 62 19
-- normal key response, the client will think it's connected as soon as it receives this
c2s 0000 Â 00 06 Â C7 B2 5A 1D Â 9D 74 27 00 Â Â Â Â Â Â Â Â Â Â ....Z..t'.
The server has just created a connection, since the syncookie was returned.
c2s 0000 Â 00 06 Â C8 6D 38 E7 Â 9D 74 27 00 Â Â Â Â Â Â Â Â Â Â ...m8..t'.
c2s 0000 Â 00 06 Â F7 80 62 19 Â 9D 74 27 00 Â Â Â Â Â Â Â Â Â Â ....b..t'.
Client responds to each of the 00 05 messages in turn.
The server ignores the first one in this case, based on the MSB of the response and gets the key from the second one.
c2s    Before decryption:
c2s 0000 Â 00 03 56 65 DF AF 98 15 71 EA 5E 67 81 66 19 95 Â ..Ve....q.^g.f..
c2s 0010 Â BE 8B 43 6B CA 79 9E C6 09 CE 8D B8 62 82 11 FC Â ..Ck.y......b...
c2s 0020 Â 99 EB 5E 12 A0 75 C7 F9 8C B9 72 35 1C D1 16 DC Â ..^..u....r5....
c2s 0030 Â C5 17 33 BE 2E 99 5A 84 4C A7 80 7B A5 CB 97 0D Â ..3...Z.L..{....
c2s 0040 Â 17 2A ED 9C 64 C2 33 93 ED B0 9D 8C 56 71 43 B5 Â .*..d.3.....VqC.
c2s 0050 Â F4 11 F8 F4 91 2B F3 75 01 CA 7B 85 56 06 6D 3F Â .....+.u..{.V.m?
c2s 0060 Â D8 F1 D5 98 5B AB 8D CC DE F5 04 Â Â Â Â Â Â Â Â Â ....[......
c2s    After decryption:
c2s 0000 Â 00 03 00 00 00 00 09 00 73 75 62 73 70 61 63 65 Â ........subspace
c2s 0010 Â 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Â ................
c2s 0020 Â 00 00 00 00 00 00 00 00 62 61 68 2C 20 6D 64 61 Â ........bah, mda
c2s 0030 Â 6E 20 68 61 63 6B 65 72 73 00 00 00 00 00 00 00 Â n hackers.......
c2s 0040 Â 00 00 00 00 00 00 00 00 A7 CF 24 47 04 2C 01 97 Â ..........$G.,..
c2s 0050 Â FB 86 00 BC 01 00 00 2B 02 00 00 A6 97 D0 33 00 Â .......+......3.
c2s 0060 Â 00 00 00 00 00 00 00 00 00 00 00 Â Â Â Â Â Â Â Â Â ...........
When client gets to the 00 02, he starts using encryption and sends a login packet.
s2c    Before decryption:
s2c 0000 Â 00 04 56 65 DF AF Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ..Ve..
s2c    After decryption:
s2c 0000 Â 00 04 00 00 00 00 Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ......
This was included to demonstrate that the server has connected with the client.
---------------------------------------------------
MERVBot build 41 connecting to ASSS with syncookies
---------------------------------------------------
Note: Build 40 and earlier did not allow 00 0e before receiving 00 02.
Demonstrates support for bots that use efficient clustering to respond to messages.
Normally this is a problem, since the cluster is encrypted before the server has
received the encryption key or any other packets. Â However, some mathematical tricks
have made it possible to get around this limitation: the second 00 05 is used now..
c2s    Connection from 127.0.0.1:1825 (proto 1)
c2s 0000 Â 00 01 B7 E6 D8 EB 01 00 Â Â Â Â Â Â Â Â Â Â Â Â Â ........
Same as VIE SubSpace.
s2c    SubSpace decryption enabled.
s2c 0000 Â 00 0E 06 00 05 C7 B2 5A 1D 06 00 05 0B B3 55 9B Â .......Z......U.
s2c 0010 Â 06 00 05 49 19 27 14 06 00 02 49 19 27 14 Â Â Â Â ...I.'....I.'.
Same as VIE SubSpace.
c2s    Before decryption:
c2s 0000 Â 00 0E 33 0B 61 52 06 C7 AA 90 90 8E 82 E1 DF 1D Â ..3.aR..........
c2s 0010 Â CB F8 F8 89 CD 84 BA AD 44 49 98 6B 2B F2 10 0F Â ........DI.k+...
c2s 0020 Â 46 11 C2 EF 48 2A 7C CA B4 A8 62 AF 8F A3 F7 D6 Â F...H*|...b.....
c2s 0030 Â 94 88 D6 D1 08 1A 67 C7 D4 EC 3B 5C 3F 2B 1E 05 Â ......g...;\?+..
c2s 0040 Â 34 FF C8 C4 9C 05 11 75 14 2B 79 9F 07 7F EA 24 Â 4......u.+y....$
c2s 0050 Â 95 BA 78 35 F4 01 DC CC C7 A0 1D 92 0F 5D 45 DD Â ..x5.........]E.
c2s 0060 Â 68 0B 3C 68 0C 48 C0 F0 8D 97 25 66 90 37 02 65 Â h.<h.H....%f.7.e
c2s 0070 Â 05 3C 99 EC D1 66 7F DC 4B E0 05 5B D2 60 92 4D Â .<...f..K..[.`.M
c2s 0080 Â 76 66 BE E4 5C D1 79 56 C1 A1 07 3F 5F 06 B0 1B Â vf..\.yV...?_...
c2s 0090 Â 5C 67 82 B3 A4 C2 A6 DB 58 F9 E2 FA E0 E1 Â Â Â Â \g......X.....
c2s 0000 Â 00 0E 0A 00 06 C7 B2 5A 1D 76 60 28 00 0A 00 06 Â .......Z.v`(....
c2s 0010 Â 0B B3 55 9B 76 60 28 00 0A 00 06 49 19 27 14 76 Â ..U.v`(....I.'.v
c2s 0020 Â 60 28 00 0E 00 05 76 60 28 00 01 00 00 00 00 00 Â `(....v`(.......
c2s 0030 Â 00 00 6B 00 03 00 00 00 00 09 00 43 61 74 69 64 Â ..k........Catid
c2s 0040 Â 2E 50 72 6F 62 65 00 00 00 00 00 00 00 00 00 00 Â .Probe..........
c2s 0050 Â 00 00 00 00 00 00 00 00 00 00 00 70 72 6F 62 65 Â ...........probe
c2s 0060 Â 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Â ................
c2s 0070 Â 00 00 00 00 00 00 00 00 00 00 00 79 92 7D 5C 04 Â ...........y.}\.
c2s 0080 Â 2C 01 00 00 86 00 BC 01 00 00 2B 02 00 00 A6 97 Â ,.........+.....
c2s 0090 Â D0 33 00 00 00 00 00 00 00 00 00 00 00 00 Â Â Â Â .3............
The difference between VIE SS and certain bots is immediately obvious.
To break it down,
00 0E
-- clustering is the difference, the following are encrypted for the server, and he doesn't have the key
   0A  00 06  C7 B2 5A 1D  76 60 28 00
-- we would like to see this field, since it contains the syncookie
   0A  00 06  0B B3 55 9B  76 60 28 00
-- this time, the second message is the key. i should have details on how this works somewhere in this text file
   0A  00 06  49 19 27 14  76 60 28 00
-- this message is ignored
More bytes follow...
s2c    Before decryption:
s2c 0000 Â 00 04 39 0B 67 95 Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ..9.g.
s2c 0000 Â 00 04 00 00 00 00 Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ......
Crazy, it worked.
--------------------------------------------------------------
BEGIN BEGIN BEGIN BEGIN ASSS enc_vie.c BEGIN BEGIN BEGIN BEGIN
--------------------------------------------------------------
This code was last updated March 17, 2004.
/* dist: public */
#include "!@#$%^&*s.h"
#include "encrypt.h"
#include "md5.h"
#define BAD_KEY (-1) /* valid keys must be positive */
/* structs */
typedef struct EncData
{
int key;
char table[520];
} EncData;
/* prototypes */
local void ConnInit(struct sockaddr_in *sin, byte *pkt, int len, void *v);
local void Init(Player *p, int k);
local int Encrypt(Player *p, byte *, int);
local int Decrypt(Player *p, byte *, int);
local void Void(Player *p);
local ClientEncryptData * ClientInit(void);
local int ClientEncrypt(ClientEncryptData *ced, byte *, int);
local int ClientDecrypt(ClientEncryptData *ced, byte *, int);
local void ClientVoid(ClientEncryptData *ced);
local void do_init(EncData *ed, int k, int len);
local int do_dec(EncData *ed, byte *data, int len);
local int do_enc(EncData *ed, byte *data, int len);
/* globals */
local int enckey;
local pthread_mutex_t mtx;
local Inet *net;
local Iplayerdata *pd;
local Iprng *prng;
u32 syncookie_randno[3];
local Iencrypt ienc =
{
INTERFACE_HEAD_INIT("__unused__", "enc-vie")
Encrypt, Decrypt, Void
};
local Iclientencrypt iclienc =
{
INTERFACE_HEAD_INIT("__unused__", "enc-vie-client")
ClientInit, ClientEncrypt, ClientDecrypt, ClientVoid
};
EXPORT int MM_enc_vie(int action, Imodman *mm, Arena *arena)
{
if (action == MM_LOAD)
{
 net = mm->GetInterface(I_NET, ALLARENAS);
 pd = mm->GetInterface(I_PLAYERDATA, ALLARENAS);
 prng = mm->GetInterface(I_PLAYERDATA, ALLARENAS);
 if (!net || !pd || !prng) return MM_FAIL;
 enckey = pd->AllocatePlayerData(sizeof(EncData*));
 if (enckey == -1) return MM_FAIL;
 mm->RegCallback(CB_CONNINIT, ConnInit, ALLARENAS);
 pthread_mutex_init(&mtx, NULL);
 mm->RegInterface(&ienc, ALLARENAS);
 mm->RegInterface(&iclienc, ALLARENAS);
 prng->GoodFillBuffer(syncookie_randno, sizeof(syncookie_randno));
 return MM_OK;
}
else if (action == MM_UNLOAD)
{
 if (mm->UnregInterface(&ienc, ALLARENAS))
 return MM_FAIL;
 if (mm->UnregInterface(&iclienc, ALLARENAS))
 return MM_FAIL;
 mm->UnregCallback(CB_CONNINIT, ConnInit, ALLARENAS);
 pd->FreePlayerData(enckey);
 mm->ReleaseInterface(net);
 mm->ReleaseInterface(pd);
 mm->ReleaseInterface(prng);
 pthread_mutex_destroy(&mtx);
 return MM_OK;
}
return MM_FAIL;
}
/* http://burtleburtle.net/bob/c/lookup2.c */
#define mix(a,b,c) \
{ \
 a -= b; a -= c; a ^= (c>>13); \
 b -= c; b -= a; b ^= (a<<8); \
 c -= a; c -= b; c ^= (b>>13); \
 a -= b; a -= c; a ^= (c>>12);  \
 b -= c; b -= a; b ^= (a<<16); \
 c -= a; c -= b; c ^= (b>>5); \
 a -= b; a -= c; a ^= (c>>3);  \
 b -= c; b -= a; b ^= (a<<10); \
 c -= a; c -= b; c ^= (b>>15); \
}
/* H(IP, port, randno), "synchronization packet cookie" */
local u32 generate_syncookie(struct sockaddr_in *sin)
{
u32 randno, ip = sin->sin_addr.S_un.S_addr;
u32 port = sin->sin_port | (sin->sin_port << 16);
randno = syncookie_randno[0];
 mix(ip, port, randno);
randno |= syncookie_randno[1];
 mix(ip, port, randno);
randno += syncookie_randno[2];
 mix(ip, port, randno);
return (ip & port) + randno;
}
void ConnInit(struct sockaddr_in *sin, byte *pkt, int len, void *v)
{
if (len == 8 && pkt[0] == 0 && pkt[1] == 1 && pkt[6] == 1 && pkt[7] == 0)
{ /* handle the initial connection request */
 u32 k = -*(int*)(pkt+2); /* session key */
 byte resp[30] = {0, 14,  6, 0, 5, 0,0,0,0,
    6, 0, 5, 0,0,0,0,
    6, 0, 5, 0,0,0,0,
    6, 0, 2, 0,0,0,0};
 struct EncData ed;
 if (k <= 0) return; /* ignore invalid keys */
 do_init(&ed, k, 9);
 *(u32*)(resp+5) = generate_syncookie(sin);
 *(u32*)(resp+12) = (k ^ *(u32*)(ed.table+14)) | 0x80000000;
 *(u32*)(resp+19) = k;
 *(u32*)(resp+26) = k;
 net->ReallyRawSend(sin, resp, 30, v);
}
else if (len >= 6 && pkt[0] == 0 && pkt[1] == 6)
{ /* handle unencrypted serial response mode */
 u32 sc = *(u32*)(pkt+2);
 Player *p;
 if (sc == generate_syncookie(sin))
 {
 ienc.head.refcount++;
 p = net->NewConnection(T_VIE, sin, &ienc, v);
 if (!p)
 {
  byte out[2] = { 0x00, 0x07 };
  net->ReallyRawSend(sin, out, 2, v);
 }
 /* we now await 2nd 00 06 to know what the key was */
 }
}
else if (len >= 24 && pkt[0] == 0 && pkt[1] == 14)
{ /* handle encrypted cluster response mode */
 struct EncData ed;
 Player *p;
 /* recover the key */
 u32 sc, key = (*(u32*)(pkt+16) ^ *(u32*)(pkt+12));
 key &= 0x7fffffff; /* must be positive */
 /* recover the syncookie */
 do_init(&ed, key, 4);
 sc = *(u32*)(ed.table+3) ^ *(u32*)(pkt+5)
 ^ ((*(u32*)(pkt+2) << 8) | (key >> 24));
 if (sc == generate_syncookie(sin))
 {
 ienc.head.refcount++;
 p = net->NewConnection(T_VIE, sin, &ienc, v);
 if (!p)
 {
  byte out[2] = { 0x00, 0x07 };
  net->ReallyRawSend(sin, out, 2, v);
return;
 }
 Init(p, key);
 }
}
else if (len >= 6 && pkt[0] == 0 && pkt[1] == 3)
{ /* a critical packet was lost before connecting */
 byte out[2] = { 0x00, 0x07 };
 net->ReallyRawSend(sin, out, 2, v);
}
}
local void do_init(EncData *ed, int k, int len)
{
int t, loop;
u16 *mytable;
ed->key = k;
if (k == 0) return;
mytable = (u16*)(ed->table);
for (loop = 0; loop < len; loop++)
{
 t = (long)(((long long)k * 0x834E0B5F) >> 48);
 t = t + (t >> 31);
 k = ((k % 0x1F31D) * 16807) - (t * 2836) + 123;
 if (!k || (k & 0x80000000)) k += 0x7FFFFFFF;
 mytable[loop] = (u16)k;
}
}
void Init(Player *p, int k)
{
EncData *ed, **p_ed = PPDATA(p, enckey);
pthread_mutex_lock(&mtx);
if (!(ed = *p_ed)) ed = *p_ed = amalloc(sizeof(*ed));
pthread_mutex_unlock(&mtx);
do_init(ed, k, 260);
}
int do_enc(EncData *ed, byte *data, int len)
{
int work = ed->key, *mytable = (int*)ed->table;
int loop, until, *mydata;
if (work == 0 || mytable == NULL) return len;
if (data[0] == 0)
{
 mydata = (int*)(data + 2);
 until = (len-2)/4 + 1;
}
else
{
 mydata = (int*)(data + 1);
 until = (len-1)/4 + 1;
}
for (loop = 0; loop < until; loop++)
{
 work = mydata[loop] ^ (mytable[loop] ^ work);
 mydata[loop] = work;
}
return len;
}
int Encrypt(Player *p, byte *data, int len)
{
EncData *ed, **p_ed = PPDATA(p, enckey);
pthread_mutex_lock(&mtx);
ed = *p_ed;
pthread_mutex_unlock(&mtx);
return ed ? do_enc(ed, data, len) : len;
}
int do_dec(EncData *ed, byte *data, int len)
{
int work = ed->key, *mytable = (int*)ed->table;
int *mydata, loop, until;
if (work == 0 || mytable == NULL) return len;
if (data[0] == 0)
{
 mydata = (int*)(data + 2);
 until = (len-2)/4 + 1;
}
else
{
 mydata = (int*)(data + 1);
 until = (len-1)/4 + 1;
}
for (loop = 0; loop < until; loop++)
{
 int tmp = mydata[loop];
 mydata[loop] = mytable[loop] ^ work ^ tmp;
 work = tmp;
}
return len;
}
int Decrypt(Player *p, byte *d, int len)
{
EncData *ed, **p_ed = PPDATA(p, enckey);
pthread_mutex_lock(&mtx);
ed = *p_ed;
pthread_mutex_unlock(&mtx);
/* syncookie: catch 2nd 00 06 containing enc.key */
if (!ed && len == 10 && d[0] == 0 && d[1] == 6)
{
 u32 k = *(u32*)(d+2);
 if (!(k & 0x80000000)) /* first one is negative */
 Init(p, k);
 return len;
}
return ed ? do_dec(ed, d, len) : len;
}
void Void(Player *p)
{
EncData *ed, **p_ed = PPDATA(p, enckey);
pthread_mutex_lock(&mtx);
ed = *p_ed;
afree(ed);
*p_ed = NULL;
pthread_mutex_unlock(&mtx);
}
ClientEncryptData * ClientInit(void)
{
EncData *ed = amalloc(sizeof(*ed));
ed->key = BAD_KEY;
return (ClientEncryptData*)ed;
}
int ClientEncrypt(ClientEncryptData *ced, byte *d, int n)
{
EncData *ed = (EncData*)ced;
if (d[1] == 0x01 && d[0] == 0x00)
{
 /* sending key init */
 /* temporarily overload the key field to be what _we_ sent */
 ed->key = *(int*)(d+2);
 return n;
}
else if (ed->key != BAD_KEY)
 return do_enc(ed, d, n);
else
 return n;
}
int ClientDecrypt(ClientEncryptData *ced, byte *d, int n)
{
EncData *ed = (EncData*)ced;
if (d[1] == 0x02 && d[0] == 0x00)
{
 /* got key response */
 int gotkey = *(int*)(d+2);
 if (gotkey == ed->key) /* signal for no encryption */
 ed->key = BAD_KEY;
 else
 do_init(ed, gotkey, 260);
 return n;
}
else if (ed->key != BAD_KEY)
 return do_dec(ed, d, n);
else
 return n;
}
void ClientVoid(ClientEncryptData *ced)
{
afree(ced);
}
--------------------------------------------------------------
END END END END END END ASSS enc_vie.c END END END END END END
--------------------------------------------------------------
--------------------------------------------------------------
BEGIN BEGIN BEGIN BEGIN patch to net.c BEGIN BEGIN BEGIN BEGIN
--------------------------------------------------------------
+ Allows encryption modules to decide for themselves which
 packets to listen for; needed to use my VIE syncookies.
+ Removes that awful "recvd data before connection established" warning
 that someone could use to flood the server logs and maybe DoS it.
 This kind of warning is fine, however, after the source of the
 packets has been authenticated by the anti-spoof protection.
--- net.c 3 Feb 2004 07:21:29 -0000 1.107
+++ net.c 18 Mar 2004 05:04:27 -0000
@@ -932,19 +930,9 @@
 if (p == NULL)
 {
- Â /* this might be a new connection. make sure it's really
- Â * a connection init packet */
- Â if (IS_CONNINIT(buf))
- Â DO_CBS(CB_CONNINIT, ALLARENAS, ConnectionInitFunc,
- Â Â (&sin, buf->d.raw, len, ld));
- Â else if (len > 1)
- Â lm->Log(L_DRIVEL, "<net> recvd data (%02x %02x; %d bytes) "
- Â Â "before connection established",
- Â Â buf->d.raw[0], buf->d.raw[1], len);
- Â else
- Â lm->Log(L_DRIVEL, "<net> recvd data (%02x; %d byte) "
- Â Â "before connection established",
- Â Â buf->d.raw[0], len);
+ Â /* this might be a new connection */
+ Â DO_CBS(CB_CONNINIT, ALLARENAS, ConnectionInitFunc,
+ Â Â (&sin, buf->d.raw, len, ld));
 goto freebuf;
 }
--------------------------------------------------------------
END END END END END END patch to net.c END END END END END END
--------------------------------------------------------------
Authors:
catid(cat02e@fsu.edu)
ICQ#18736684
AIM:MrCatid
irc.freenode.net #sheepcloning Catid