UOEncryption
library api guide
Huffman
code by:
Tables from Ultimate Melange
Daniel ‘Necr0Potenc3’ Cavalcanti
Login encryption code by:
Sphere gray server source code + Injection’s source code (Beosil and Infidel I think)
Daniel ‘Necr0Potenc3’ Cavalcanti (code cleanup and C++ -> pure C conversion)
Login encryption get keys code by:
RElf (unknown work… maybe crypto math?)
Alexander ‘Mamaich’ (coding)
Twofish and Blowfish code by:
Bruno ‘Beosil’ Heidelberger (original code)
Luke ‘Infidel’ Dunstan (maintainer)
Roman ‘Fallout’ Ilichev (code cleanup of Twofish)
Daniel ‘Necr0Potenc3’ Cavalcanti (code cleanup and C++ -> pure C conversion)
MD5 code by:
Aladdin Enterprises (base MD5 code)
Daniel ‘Necr0Potenc3’ Cavalcanti (reverse engineering of the client and code enhancement)
Description of Ultima Online’s
encryption
The uo client connects to the login server which then redirects the client to a game server, thus we have a login socket and a game socket.
Note: the login server and game server can be the same, such thing happens in free shards.
LOGIN:
The first encryption is given in the login socket. It’s client->server only. The server->client stream has no encryption or compression.
The first login encryption code dates from the 1.25.35 and bellow clients. The second encryption code is used only in version 1.25.36 and uses a especial set of 6 keys. All other versions of Ultima Online clients, including the current ones, use the enhanced version of the first type of login encryption. Suffice to say it is simple and well coded.
The UOEncryption library supports all 3 types of login encryption but currently only the most common one, used in the current days, is exported for usage.
GAME:
The server->client stream is always compressed with Huffman. Huffman tables used by the UO client are static.
The UO client uses Blowfish, Blowfish + Twofish or Twofish + MD5.
On the first two cases (Blowfish, Blowfish + Twofish) the encryption is done only on the client->server stream. On the third case (Twofish + MD5), Twofish is applied to the client->server stream while MD5 is applied to the server->client stream.
Blowfish is used in 2.0.0 and bellow clients. Blowfish + Twofish is used in 2.0.0x to 2.0.3 clients. Client 2.0.4 and above use Twofish + MD5.
The MD5 server->client encryption is new and until the release of UOEncryption, unknown. It was reversed engineered by Daniel ‘Necr0Potenc3’ Cavalcanti. It works by generating the digest (MD5’s 16 bytes table) using Twofish’s subData3 array. The digest is then used to xor the server->client packets after the packets have been compressed with Huffman. Very simple and well thought.
Note: Blowfish is the only part of UO’s encryption that does not depend on anything, even the game seed is useless for it.
API Guide
Login crypt:
Whenever the login socket is created, you have to reset the login crypt. It is done by:
LoginCryptObj
LCObj; /* a global var */
/* in the
initialization routine */
LCObj.pseed = seed; /* the seed is the first packet in the socket. Always 4 bytes long */
LCObj.k1 = login key1; /* you have to find or set this */
LCObj.k2 = login key2;
LoginCryptInit(&LCObj);
The encryption and decryption is done by:
LoginCryptEncrypt(&LCObj, &Inbuf, &Outbuf, len);
Note: Inbuf and Outbuf can be the same. Len is the size of Inbuf. Outbuf should have the same size or greater. If you are using both encryption and decryption, remember to create a LoginCryptObj for each.
Game crypt (Huffman):
Huffman does not need to be initialized unless you wish to use support for incomplete codewords. The incomplete codewords issue is something which I never saw happening but I included it in the library just to make it complete. You can call the Huffman apis by setting the HuffmanObj pointer to NULL if you do not wish to use it.
If you do wish to use the incomplete codeword support, you have to declare the HuffmanObj and initialize it.
HuffmanObj HuffObj; /* global var */
/* in the initialization function */
DecompressClean(&HuffObj);
To compress packets:
Compress(&Outbuf, &InBuf, &OutbufSize, &InbufSize);
Note: Inbuf contains the data to be compressed, Outbuf will hold it. Outbufsize should contain Outbuf’s size in bytes. After Compress is called, Outbufsize will hold how many bytes were transferred to Outbuf. InbufSize should hold the size of Inbuf.
Note2: Outbuf’s size should be the double of Inbuf’s size. For safety, since compression will not always compress.
For decompression:
Decompress(&Outbuf, &Inbuf, &OutbufSize, &InbufSize, &HuffObj);
Note: Inbuf contains the data to be decompressed, Outbuf will hold it. Outbufsize should contain the size of Outbuf. Inbufsize should hold the size of Inbuf. Huffobj is the HuffmanObj to be used in case incomplete codewords support is to be used. It can be set to NULL so it won’t be used.
Note2: Outbuf’s size should be ((Inbuf’s size * 4) + 4)
Game crypt (Blowfish):
Upon the creation of the game socket you have to reset the encryption:
BlowfishObj BFObj; /* global var */
/* in the initialization routine */
BlowfishInit(&BFObj);
Encryption/Decryption:
BlowfishEncrypt(&BFObj, &Inbuf, &OutBuf, len);
Note: InBuf and Outbuf can be the same. Len is the size of Inbuf. Outbuf should have the same size or greater. If you are using both encryption and decryption, remember to create a BlowfishObj for each.
Game crypt (Blowfish
+ Twofish):
The initialization is done by:
/* globals */
BlowfishObj BFObj;
TwofishObj TFObj;
/* in the init function */
BlowfishInit(&BFObj);
TFObj.IP = seed; /* the seed of the game socket */
TwofishInit(&TFObj);
Encryption:
BlowfishEncrypt(&BFObj, &Inbuf, &Outbuf, len);
TwofishEncrypt(&TFObj, &Inbuf, &Outbuf, len);
Decryption:
TwofishEncrypt(&BFObj, &Inbuf, &Outbuf, len);
BlowfishDecrypt(&BFObj, &Inbuf, &Outbuf, len);
Note: Inbuf and Outbuf can be the same. Len is the size of Inbuf.
Game crypt (Twofish +
MD5):
Initialize twofish and md5 at the start of the socket.
/* globals */
TwofishObj TFObj;
MD5Obj MDObj;
/* at the initialize function */
TFObj.IP = seed; /* 4 bytes please */
TwofishInit(&TFObj);
MD5Init(&MDObj, &TFObj.subData3[0], 256);
Twofish encryption/decryption:
TwofishEncrypt(&TFObj, &Inbuf, &Outbuf, len);
MD5 encryption/decryption:
MD5Encrypt(&MDObj, &Inbuf, &Outbuf, len);
APIs
int
CalculateKeys(unsigned char *Plaintext, unsigned char *Ciphertext, unsigned int
*Seed, unsigned int *Key1, unsigned int *Key2)
unsigned char *Plaintext – Contains the non-encrypted look of the Ciphertext
unsigned char *Ciphertext – Contains the encrypted data
unsigned int *LoginSeed – Contains the seed
unsigned int *ClientLoginKey1 – Receives the login key 1
unsigned int *ClientLogin Key2 – Receives the login key 2
Calculates the login keys using the Ciphertext buffer for the crypt math. Compare s the decrypted Ciphertext with the Plaintext and returns 1 on success, -1 on failure. Both Plaintext and Ciphertext must be *AT LEAST* 61 bytes long. The values are passed to ClientLoginKey1 and ClientLoginKey2 independent of the function succeeding or not.
void
LoginCryptInit(LoginCryptObj *obj)
LoginCryptObj *Obj – A pointer to a LoginCryptObj structure
Initializes the crypt info of the ‘obj’ structure. The pseed value of the structure must be declared. Has to be called before calling the LoginCryptEncrypt.
void LoginCryptEncrypt(LoginCryptObj *obj, unsigned char *in, unsigned
char *out, int len)
LoginCryptObj *Obj – A pointer to a LoginCryptObj structure
unsigned char *in – A pointer to a buffer with the data to be encrypted
unsigned char *out – A pointer to a buffer to dump the encrypted data
int len – The amount of bytes in the ‘in’ buffer to be encrypted
Encrypts and decrypts login packets. Uses the encryption method used in clients greater than 1.25.36. The ‘out’ buffer must be of the same size or greater than ‘len’
void Compress(char
*dest, const char *src, int *dest_size, int *src_size)
char *dest – A pointer to a buffer to dump the compressed data
const char *src – A pointer to a buffer with the data to be compressed
int *dest_size – A pointer to the integer with the size of ‘dest’ in bytes
int *src_size – A pointer to the integer with the amount of bytes to be compressed in ‘src’
Compresses data using UO’s Huffman tables. When the compression is done ‘dest_size’ will hold the amount of bytes in ‘dest’.
void
DecompressClean(HuffmanObj *obj)
HuffmanObj *obj – A pointer to a HuffmanObj structure
Should only be used if the user intends to support incomplete codewords. Not really necessary. Has to be called before calling the Decompress function when passing a ‘obj’ parameter.
void Decompress(char
*dest, const char *src, int *dest_size, int *src_size, HuffmanObj *obj)
char *dest – A pointer to a buffer to dump the decompressed data
const char *src – A pointer to a buffer with the data to be decompressed
int *dest_size – A pointer to the integer with the size of ‘dest’ in bytes
int *src_size – A pointer to the integer with the amount of bytes to be decompressed in ‘src’
Decompresses data by using UO’s Huffman table. The ‘obj’ pointer can be set to NULL if the user does not intend to use the incomplete codeword support. But if he does, DecompressClean should be called in the start of the socket.
void
BlowfishInit(BlowfishObj *Obj)
BlowfishObj *Obj – A pointer to a BlowfishObj structure
No values of the ‘Obj’ structure has to be declared. Has to be called before calling the other Blowfish functions.
void
BlowfishEncrypt(BlowfishObj *Obj, unsigned char *in, unsigned char *out, int
len)
BlowfishObj *Obj – A pointer to a BlowfishObj structure
unsigned char *in – A pointer to a buffer with the data to be encrypted
unsigned char *out – A pointer to a buffer to dump the encrypted data
int len – The amount of bytes in the ‘in’ buffer to be encrypted
Encrypts data… like… what else did you expect? (ROFLMAO) Encrypts data in the game socket.
void BlowfishDecrypt(BlowfishObj
*Obj, unsigned char *in, unsigned char *out, int len)
BlowfishObj *Obj – A pointer to a BlowfishObj structure
unsigned char *in – A pointer to a buffer with the data to be decrypted
unsigned char *out – A pointer to a buffer to dump the decrypted data
int len – The amount of bytes in the ‘in’ buffer to be decrypted
Decrypts Blowfish-encrypted-data in the game socket.
void
TwofishInit(TwofishObj *Obj)
TwofishObj *Obj – A pointer to a TwofishObj structure
No values of the ‘Obj’ structure has to be declared. Has to be called before calling the TwofishEncrypt function.
void TwofishEncrypt(BlowfishObj
*Obj, unsigned char *in, unsigned char *out, int len)
TwofishObj *Obj – A pointer to a TwofishObj structure
unsigned char *in – A pointer to a buffer with the data to be encrypted
unsigned char *out – A pointer to a buffer to dump the encrypted data
int len – The amount of bytes in the ‘in’ buffer to be encrypted
void MD5Init(MD5Obj
*Obj, unsigned char *Data, unsigned int Size)
MD5Obj *Obj – A pointer to a MD5Obj structure
unsigned char *Data – A pointer to a buffer
unsigned int Size – Amount of bytes in the buffer
If used in UO (duh…) the ‘Data’ should be a subData3 table generated by TwofishInit. This will generate a MD5 Digest (a 16 byte table) used by MD5Encrypt to xor the bytes to be encrypted.
void
MD5Encrypt(MD5Obj *Obj, unsigned char *in, unsigned char *out, int len)
MD5Obj *Obj – A pointer to a MD5Obj structure
unsigned char *in – A pointer to a buffer with the data to be encrypted
unsigned char *out – A pointer to a buffer to dump the encrypted data
int len – The amount of bytes in the ‘in’ buffer to be encrypted
Used both for encryption and decryption.
void
PWEncrypt(unsigned char *in, unsigned char *out, int len)
void
PWDecrypt(unsigned char *in, unsigned char *out, int len)
unsigned char *in – A pointer to a buffer with the data to be encrypted
unsigned char *out – A pointer to a buffer to dump the encrypted data
int len – The amount of bytes in the ‘in’ buffer to be encrypted
PWEncrypt will encrypt the data and PWDecrypt will decrypt… well that couldn’t be more obvious… heh
GREET THOSE WHO DESERVE. SHARE WITH THOSE WHO SEEK.
OPEN YOUR MIND AND WE’LL OPEN YOU A NOT A NEW WORLD… BUT A WORLD YOU DIDN’T
KNOW IT WAS THERE.
-gR
alle x90
for we are not to be named…