As part of the initial negotiation between the client and hub, the hub sends a $Lock <lock> Pk=<pk> command and the client must respond with a $Key <key> command. Until the DCN encoding, which we'll discuss last, the <key> has exactly as many characters as the <lock>.
Except for the first, each key character is computed
from the corresponding lock character and the one before
it. If the first character has index 0 and the lock has a length
of len then:
for (i = 1; i < len; i++) key[i] = lock[i] xor lock[i-1];
The first key character is calculated from the first lock character and the last two lock characters:
key[0] = lock[0] xor lock[len-1] xor lock[len-2] xor 5
Next, every character in the key must be nibble-swapped:
for (i = 0; i < len; i++) key[i] = ((key[i]<<4) & 240) | ((key[i]>>4) & 15)
Finally, the characters with the decimal ASCII values of 0, 5, 36, 96, 124, and 126 cannot be sent to the server. Each character with this value must be substituted with the string /%DCN000%/, /%DCN005%/, /%DCN036%/, /%DCN096%/, /%DCN124%/, or /%DCN126%/, respectively. The resulting string is the key to be sent to the server.
If your programming language of choice doesn't have xor or
shift operations on characters, convert them to integers. If it
doesn't have a bit shift at all, then x << y = x*(2**y)
and x >> y = x/(2**y) for all integers x and y
(** is the exponent operation or "to the power of"). Be sure to use unsigned values
everywhere and do not do sign extension. Shift operations
always lose the high or low bit (they are not roll operations!).
The & (and) and | (or) operations are all logical, not boolean
(eg. 6 & 13 = 4, not 1).
When the hub connects to the hublist, it must undergo a
similar Lock/Key negotiation. The <key> calculation is
the same, but the special number 5 in the second step is
replaced with the following value is not computed from the hub's listening port,
but rather the random outgoing port that the winsock selects.
localport:
((localport>>8) + (localport & 255)) & 255