/* Save initial server packet for auth hmac generation. 1024 bytes should be enough. */
ByteBuffer serverPacketBuffer = ByteBuffer.allocate(1024);
/* Read server random (first 2 bytes). */
if((ret = this.receive(this.session.serverRandom, 0, 2, "server random packet")) == -1){
throw new DespotifyException("Failed to read server random.");
}
/* Check if we got a status message. */
if(this.session.serverRandom[0] != 0x00 || ret != 2){
int errorCode;
/*
* Substatuses:
* 0x01 : Client upgrade required.
* 0x03 : Non-existant user.
* 0x04 : Account has been disabled.
* 0x06 : You need to complete your account details.
* 0x09 : Your current country doesn't match that set in your profile.
* Default : Unknown error
*/
StringBuilder message = new StringBuilder(250);
switch (this.session.serverRandom[1]) {
case 0x01:
message.append("Client upgrade required: ");
case 0x03:
message.append("Non-existant user.");
case 0x04:
message.append("Account has been disabled.");
case 0x06:
message.append("You need to complete your account details.");
case 0x09:
message.append("Your current country doesn't match that set in your profile.");
}
if (message.length() == 0) {
message.append("Unknown error.");
}
/* If substatus is 'Client upgrade required', read upgrade URL. */
if(this.session.serverRandom[1] == 0x01){
if((ret = this.receive(buffer, 0x11a, "text message length")) > 0){
paddingLength = buffer[0x119] & 0xFF;
if((ret = this.receive(buffer, paddingLength, "text message")) > 0){
message.append(new String(Arrays.copyOfRange(buffer, 0, ret)));
}
}
}
message.append(" (").append(String.valueOf(ret)).append(")");
throw new AuthenticationException(message.toString());
}
/* Read server random (next 14 bytes). */
if((ret = this.receive(this.session.serverRandom, 2, 14, "14 random server bytes")) != 14){
throw new DespotifyException("Failed to read server random. ("+ret+")");
}
/* Save server random to packet buffer. */
serverPacketBuffer.put(this.session.serverRandom);
/* Read server public key (Diffie Hellman key exchange). */
if((ret = this.receive(buffer, 96, "public server key")) != 96){
throw new DespotifyException("Failed to read server public key. ("+ret+")");
}
/* Save DH public key to packet buffer. */
serverPacketBuffer.put(buffer, 0, 96);
/*
* Convert key, which is in raw byte form to a DHPublicKey
* using the DHParameterSpec (for P and G values) of our
* public key. Y value is taken from raw bytes.
*/
this.session.dhServerPublicKey = DH.bytesToPublicKey(
this.session.dhClientKeyPair.getPublicKey().getParams(),
Arrays.copyOfRange(buffer, 0, 96)
);
/* Read server blob (256 bytes). */
if((ret = this.receive(this.session.serverBlob, 0, 256, "server blob")) != 256){
throw new DespotifyException("Failed to read server blob. ("+ret+")");
}
/* Save RSA signature to packet buffer. */
serverPacketBuffer.put(this.session.serverBlob);
/* Read salt (10 bytes). */
if((ret = this.receive(this.session.salt, 0, 10, "salt")) != 10){
throw new DespotifyException("Failed to read salt. ("+ret+")");
}
/* Save salt to packet buffer. */
serverPacketBuffer.put(this.session.salt);
/* Read padding length (1 byte). */
if((paddingLength = this.receive("padding length")) == -1){
throw new DespotifyException("Failed to read paddling length.");
}
/* Save padding length to packet buffer. */
serverPacketBuffer.put((byte)paddingLength);
/* Check if padding length is valid. */
if(paddingLength <= 0){
throw new DespotifyException("Padding length is negative or zero.");
}
/* Read username length. */
if((usernameLength = this.receive("")) == -1){
throw new DespotifyException("Failed to read username length.");
}
/* Save username length to packet buffer. */
serverPacketBuffer.put((byte)usernameLength);
/* Read lengths of puzzle challenge and unknown fields */
this.receive(buffer, 8, "length of puzzle challenge + length of 3 unknown fields");
/* Save bytes to packet buffer. */
serverPacketBuffer.put(buffer, 0, 8);
/* Get lengths of puzzle challenge and unknown fields. */
ByteBuffer dataBuffer = ByteBuffer.wrap(buffer, 0, 8);
int puzzleChallengeLength = dataBuffer.getShort();
int unknownLength1 = dataBuffer.getShort();
int unknownLength2 = dataBuffer.getShort();
int unknownLength3 = dataBuffer.getShort();
/* Read padding. */
if((ret = this.receive(buffer, paddingLength, "padding")) != paddingLength){
throw new DespotifyException("Failed to read padding. (" + ret + ")");
}
/* Save padding (random bytes) to packet buffer. */
serverPacketBuffer.put(buffer, 0, paddingLength);
/* Read username into buffer and copy it to 'session.username'. */
if((ret = this.receive(buffer, usernameLength, "username")) != usernameLength){
throw new DespotifyException("Failed to read username. (" + ret + ")");
}
/* Save username to packet buffer. */
serverPacketBuffer.put(buffer, 0, usernameLength);
/* Save username to session. */
this.session.username = Arrays.copyOfRange(buffer, 0, usernameLength);
/* Receive puzzle challenge and unknown bytes. */
this.receive(buffer, 0, puzzleChallengeLength, "puzzle challange data");
this.receive(buffer, puzzleChallengeLength, unknownLength1, "unknown field 1");
this.receive(buffer, puzzleChallengeLength + unknownLength1, unknownLength2, "unknown field 2");
this.receive(buffer, puzzleChallengeLength + unknownLength1 + unknownLength2, unknownLength3, "unknown field 3");
/* Save to packet buffer. */
serverPacketBuffer.put(buffer, 0, puzzleChallengeLength + unknownLength1 + unknownLength2 + unknownLength3);
serverPacketBuffer.flip();
/* Write data from packet buffer to byte array. */
this.session.initialServerPacket = new byte[serverPacketBuffer.remaining()];
serverPacketBuffer.get(this.session.initialServerPacket);
/* Wrap buffer in order to get values. */
dataBuffer = ByteBuffer.wrap(buffer, 0, puzzleChallengeLength + unknownLength1 + unknownLength2 + unknownLength3);
/* Get puzzle denominator and magic. */
if(dataBuffer.get() == 0x01){
this.session.puzzleDenominator = dataBuffer.get();
this.session.puzzleMagic = dataBuffer.getInt();
}
else{
throw new DespotifyException("Unexpected puzzle challenge.");
}
}