Club Penguin:Protocol

From HashNet Wiki
Jump to: navigation, search

Club Penguin's Protocol is quite convoluted, but here's a run-down of how it works. When the client wants to initially log-in to Club Penguin, it must first talk to a login server (process) and then get a login token which can then be used to login to any world server (process). All packets are terminated with the \0 character, or the NULL character. This means at the end of each message, a \0 character is appended, by both the client and the server. All packets are also in plain-text. Messages are not encrypted.

All XML messages/packets should have the following form:

<msg t="MessageType"><body action="ActionType" r="IntRoomId"></body></msg>

  • MessageType: Usually always 'sys' for System.
  • ActionType: What action is being requested. For example; rndK (random key), apiOK (version check is ok), etc.
  • IntRoomId: The internal room ID from which this message is being sent from. Usually always 0 or -1 for login handshakes.

All XT messages/packets should have the following form:

%Type%Handle%IntRoomId%Data%

  • Type: Message type; Always 'xt'.
  • Handle: What handler should handle this data, for example; jr (join room), sp (send player), sp (send position), etc.
  • IntRoomID: The internal room ID from which this message is being sent from. usually always 0 or -1 for login handshakes.
  • Data: Optional data delimited by the '%' or '|' or ',' characters.


Login Server

Logging in to a Club Penguin server is not too difficult as long as you have the resources and knowledge behind the process of it.

The full process looks something like this:

  1. Client Connects
  2. Client: <policy-file-request/>
  3. Server: <cross-domain-policy><allow-access-from domain="*" to-ports="6110" /></cross-domain-policy>
  4. Client Disconnects
  5. Client Connects
  6. Client: <msg t='sys'><body action='verChk' r='0'><ver v='153' /></body></msg>
  7. Server: <msg t="sys"><body action="apiOK" r="0"></body></msg>
  8. Client: <msg t='sys'><body action='rndK' r='-1'></body></msg>
  9. Server: <msg t="sys"><body action="rndK" r="-1"><k>xy0fehaycu9copomye3tideddep6nuca</k></body></msg>
  10. Client: <msg t='sys'><body action='authentication' r='0'><auth><hash><![CDATA[io0otmague0diczlov3gotfuzbo4raid]]></k></auth></body></msg>
  11. Server: %xt%auth%-1%success%
  12. Client: <msg t='sys'><body action='login' r='0'><login z='w1'><nick><![CDATA[username]]></nick><pword><![CDATA[exe6anotychi4volsltak9sthitil9ey]]></pword></login></body></msg>
  13. Server: %xt%gs%-1%113,0|0,0|100,2|1000,0%
  14. Server: %xt%l%-1%1316248%Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed%%113,0|0,0|100,2|1000,0%
  15. Client Disconnects

Here's an explanation of each part:

  1. The client connects to the login server.
  2. The client sends a Flash policy-file-request.
  3. The server should respond with an XML message containing what domain we are allowed to connect from and which ports. Which in this case will allow connecting to any domain on port 6110.
  4. The client should disconnect.
  5. If the client matches the requirements by the cross-domain-policy specified by the server then it should reconnect and continue the login process.
  6. The client must then send a verChk or "version check" packet which will ask the server if the current client protocol version is appropriate to use to communicate with this server.
  7. The server must then respond with either apiOK which means this version is good to use, or apiKO which means this client version is not ok to use. If the servers sends back an apiKO the client should disconnect and cease attempting to connect to the server with that protocol version implementation.
  8. The client must then send an rndK or random key request.
  9. the server should respond with a string of 32-characters of random characters.
  10. This step depends highly on the server's implementation of authentication and not all login server emulators may follow the same schema, however in our case luckily the client must send a XML message with a static authentication token to the server, in this case it is always going to be io0otmague0diczlov3gotfuzbo4raid.
  11. The server should then respond with the first XT message with the XT handle of auth, internal room id as -1, and the data as success if the first stage of authentication was successful. Any other message should be considered as failing the first stage of authentication, and the client should disconnect if that is the case.
  12. The client should then send an XML message containing its username and hashed password to the server. With username being the penguin's username in plain-text and exe6anotychi4volsltak9sthitil9ey being the hashed password.
  13. If login was successful the server should reply with an XT message with the XT handle of gs- "get servers", internal room as -1, and the data being a string of numbers delimited by , and | characters. The first argument being the server ID, and the second argument being the population number.
  14. Finally the server will send your player ID and login token in a XT message with the XT handle of l- "login", internal room as -1, and in this case the data 1316248 (player ID) and Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed (login token). We must use the login token to authenticate with world servers.
  15. The client disconnects from the login server.

The client should now have these values:

Key Value
Username username
Password password
Player ID 1316248
Login Token Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed
Servers
Server ID Population Value
113 0
0 0
100 2
1000 0


ActionScript source from airtower.swf in the Club Penguin Client.

function getLoginHash() {
	var _loc2_ = this.encryptPassword(this.password).toUpperCase();
	_loc2_ = this.encryptPassword(_loc2_ + this.rand_key + this.encryptPassword((_loc2_ = _loc2_ + this.password) + this.encryptPassword(this.password = this.password + _loc2_)));
	return _loc2_;
}

function hex_md5(s) {
	return com.clubpenguin.crypto.MD5.hash(s);
}

function encryptPassword(pass) {
	var _loc1_ = com.clubpenguin.crypto.MD5.hash(pass);
	_loc1_ = _loc1_.substr(16,16) + "" + _loc1_.substr(0,16);
	return _loc1_;
}


An example implementation of Club Penguin's password hashing with Lua, using kikito's MD5 library.

local function passwdObfuscation(password)
	local md5pass = md5.sumhexa(password)
	local hash = md5pass:sub(-16, 32) .. "" .. md5pass:sub(0, 16)
	return hash
end

local function getLoginHash(password, key)
	local hash = passwdObfuscation(password):upper()
	local const = "Y(02.>'H}t\":E1"
	hash = passwdObfuscation(hash .. key .. const)
	return hash
end

local hash = getLoginHash("password", "xy0fehaycu9copomye3tideddep6nuca")

In the example above, the variable 'hash' is what should be sent back to the server as an XML message at stage 12.

At stage 13 we should be getting a XT handle of gs or "get servers" message, which will have a list of servers and their population size, in this case 113,0|0,0|100,2|1000,0.

A more detailed view:

ServerID_1,Population_1 | ServerID_2,Population_2 | ServerID_3,Population_3 ... etc.

In this case, world server ID 100 has a population number of 2, which depends on server implementation, but in default implementations means roughly ~10 players.

World Server

The process of logging in to a world server looks something like this:

  1. Client Connects
  2. Client: <policy-file-request/>
  3. Server: <cross-domain-policy><allow-access-from domain="*" to-ports="6112" /></cross-domain-policy>
  4. Client Disconnects
  5. Client Connects
  6. Client: <msg t='sys'><body action='verChk' r='0'><ver v='153' /></body></msg>
  7. Server: <msg t="sys"><body action="apiOK" r="0"></body></msg>
  8. Client: <msg t='sys'><body action='rndK' r='-1'></body></msg>
  9. Server: <msg t="sys"><body action="rndK" r="-1"><k>fmKXtY2Lj</k></body></msg>
  10. Client: <msg t='sys'><body action='login' r='0'><login z='w1'><nick><![CDATA[username]]></nick><pword><![CDATA[fc3ca6df63d0db8939aaffd1c36cfc5dUk4J8L+YKINxbQYpyLB31ZnA5seEXf]]></pword></login></body></msg>
  11. Server: %xt%l%-1%Club Penguin Continued%
  12. Client: %xt%s%j#js%-1%1316248%Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed%en% - Join Server using login token
  13. Server: %xt%js%-1%1%0%1%1% - Join Server Settings
  14. Server: %xt%gps%-1%1316248%|242|238|230|244|212|214|216|206|220|104|93|10|15% - Get Puffle Status
  15. Server: %xt%lp%-1%1316248|username|45|4|414|0|0|14151|0|6002|501|9056|0|0|1|1|876%1149237%0%1440%1557114377748%158%0%158%%7% - Load Player
  16. Server: %xt%jr%400%2093%1316248|username|45|4|414|0|0|14151|0|6002|501|9056|0|0|1|1|876% - Join Room 2093 (internal id 400)
  17. Server: %xt%ap%400%1316248|username|45|4|414|0|0|14151|0|6002|501|9056|0|0|1|1|876% - Add Player (internal id 400)
  18. Server: %xt%pg%-1%
  19. Server: %xt%pgu%-1%%
  20. Client: %xt%s%i#gi%-1% - Get Inventory
  21. Server: %xt%gi%-1%1285%9106% - New Player Baseball Hat and New Player Background
  22. Client: %xt%s%b#gb%-1%
  23. Server: %xt%gb%-1%%
  24. Client: %xt%s%n#gn%-1%
  25. Server: %xt%gn%-1%
  26. Client: %xt%s%l#mst%-1%
  27. Server: %xt%mst%-1%0%3%
  28. Client: %xt%s%l#mg%-1%
  29. Client: %xt%s%p#pgu%-1%
  30. Client: %xt%s%u#glr%-1% - Get Server Version
  31. Server: %xt%glr%-1%Aurora 0.3.8% - Aurora 0.3.8
  32. Client: %xt%s%i#qpa%-1%1316248%
  33. Client: %xt%s%f#epfga%-1%
  34. Server: %xt%epfga%-1%1%
  35. Server: %xt%pg%-1%
  36. Server: %xt%pgu%-1%%
  37. Server: %xt%mg%-1%sys|0|242|1890|1557084994245|4%sys|0|12||1553487395826|3%sys|0|177||1543487436776|1% - Postcards (sys being the system sending the postcard to the player)
  38. Server: %xt%qpa%-1%1316248%821|8006|8009%
  39. Client: %xt%s%f#epfgr%-1%
  40. Server: %xt%epfgr%-1%0%0%
  41. Client: %xt%s%f#epfgf%-1%
  42. Server: %xt%epfgf%-1%%

Load Player Details

The "Load Player" packet should contain these values:

Key Value
Player Status
Delimited by the | character
Key Value
ID The player's ID.
Username The player's username.
N/A 45 (constant).
Color Penguin color.
Head Item Penguin item worn on head.
Face Item Penguin item worn on face.
Neck Item Penguin item worn on neck.
Body Item Penguin item worn on body.
Hand Item Penguin item worn on hand.
Feet Item Penguin item worn on feet.
Flag Flag pin in inventory.
Photo Background in inventory.
X coord X position of player.
Y coord Y position of player.
Frame Which frame this player should play.
Is Member 1 if the player is a member, 0 if the player is not a member.
Rank Player Club_Penguin:Badge rank in inventory.
Coins An unsigned integer of how many coins this player has.
Safe Chat 1 if chat should be disabled or 0 if chat should stay enabled.
Egg Timer 1440 (Max Egg Timer time.)
Penguin Standard Time An unsigned integer of how many milliseconds have passed since the epoch.
Penguin Age An unsigned integer of how many days have passed since account creation.
N/A 0 (constant).
Penguin Age Again, an unsigned integer of how many days have passed since account creation.
N/A (constant).
Time Zone Offset The timezone offset from GMT to use. For example 7 for PST.


XT Definitions

Bind Type will either be login for Login Server, world for World Server, client for Client, or both for both World Server and Client.

Bind Type Handle Example Data Definition
login auth success Authentication; authentication response from the server, data should be set to "success" if the first stage of authentication was successful.
login gs 0,0|100,2|1000,0 Get Servers; the server sends a list of world server id's and their coorisponding populations. In this case server id 100, has 2 players.
login l 1316248%Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed Login Success; the client can now use their player id (in this case 1316248), their login token (in this case Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed) to login to world servers.
world l Club Penguin Continued Login Success; the client has successfully logged in, now the server and client can exchange player information.
world js 1%0%1%1 Join Server; the server sends this packet telling the client it has now been fully authenticated and will begin to send player information. The second data field 0 is the EPF Agent Status and the third data field 1 is if the player should have access to moderator tools, 1 being true, and 0 being false.
world lp Load Player; the server sends information on the player state. Player ID|username|45|Player Color|Head Item|Face Item|Neck Item|Body Item|Hand Item|Feet Item|Flag Item|Photo Item|X position|Y position|Frame|Is Member|Badge Rank%Coins%Safe Chat%1440%Current Time With Millis%Penguin Age%0%Penguin Age%%Server Timezone Offset.
both ems -----BEGIN PUBLIC KEY----- Encryption Request; the server sends the client a public RSA key to use to encrypt an AES secret the client creates to chat encrypted. If the client wishes to initiate an encrypted chat, it must respond with their secret AES key encrypted in the PGP key provided. Once the server has the client's AES key, it will then send encrypted messages over the em handle.
both em Encrypted Message; the server and client are sending encrypted chat messages. Also see ems.
both h Heartbeat; usually initiated by the client, the server will respond with another heartbeat to ensure the connection stays alive.
client j#js 1316248%Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed%en Join Server; the client sends the server a request to join with their player id (in this case 1316248), their login token (in this case Sod/aiNsaCfU)exCnAkcS|nYEggN,Bed) and their language (in this case en).
client j#jr 2074 Join Room; join room request to external room id 2074.
world uet Egg Timer for the user (Parental Controls). it expects a time between 1 - 1440 minutes, if the Number is above it will Deactivate the Egg Timer since the Egg Timer runs per day.