/* Receive initial packet (key exchange). */ public void receiveInitialPacket() throws DespotifyException { byte[] buffer = new byte[512]; int ret, paddingLength, usernameLength; /* 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."); } }