@Override public void run() { try { // Authenticate URLConnection connection = new URL(this.postURL).openConnection(); JsonObject json; try (InputStream is = connection.getInputStream()) { if (is.available() == 0) { this.session.disconnect("Invalid username or session id!"); return; } try { json = gson.fromJson(new InputStreamReader(is), JsonObject.class); } catch (Exception e) { LanternGame.log().warn("Username \"" + this.username + "\" failed to authenticate!"); this.session.disconnect("Failed to verify username!"); return; } } String name = json.get("name").getAsString(); String id = json.get("id").getAsString(); // Parse UUID UUID uuid; try { uuid = UUIDHelper.fromFlatString(id); } catch (IllegalArgumentException e) { LanternGame.log().error("Returned authentication UUID invalid: " + id, e); this.session.disconnect("Invalid UUID."); return; } JsonArray propsArray = json.getAsJsonArray("properties"); // Parse properties Multimap<String, ProfileProperty> properties = LinkedHashMultimap.create(); for (JsonElement element : propsArray) { JsonObject json0 = element.getAsJsonObject(); String propName = json0.get("name").getAsString(); String value = json0.get("value").getAsString(); String signature = json0.has("signature") ? json0.get("signature").getAsString() : null; properties.put(propName, new LanternProfileProperty(propName, value, signature)); } LanternGame.log().info("Finished authenticating."); this.session.messageReceived( new MessageLoginInFinish(new LanternGameProfile(uuid, name, properties))); } catch (Exception e) { LanternGame.log().error("Error in authentication thread", e); this.session.disconnect("Internal error during authentication.", true); } }
@Override public void handle(NetworkContext context, MessageForgeHandshakeInOutAck message) { Session session = context.getSession(); Attribute<ForgeServerHandshakePhase> phase = context.getChannel().attr(ForgeHandshakePhase.PHASE); switch (phase.get()) { case WAITING_ACK: if (!message.getPhase().equals(ForgeClientHandshakePhase.WAITING_SERVER_DATA)) { session.disconnect( "Retrieved unexpected forge handshake ack message. (Got " + message.getPhase() + ", expected " + ForgeClientHandshakePhase.WAITING_SERVER_DATA + ")"); } else { List<MessageForgeHandshakeOutRegistryData.Entry> entries = Lists.newArrayList(); entries.add( new MessageForgeHandshakeOutRegistryData.Entry( "fml:items", Maps.newHashMap(), Lists.newArrayList())); entries.add( new MessageForgeHandshakeOutRegistryData.Entry( "fml:blocks", Maps.newHashMap(), Lists.newArrayList())); session.send(new MessageForgeHandshakeOutRegistryData(entries)); session.send(new MessageForgeHandshakeInOutAck(ForgeServerHandshakePhase.WAITING_ACK)); phase.set(ForgeServerHandshakePhase.COMPLETE); } LanternGame.log() .info( "{}: Forge handshake -> Received ack (waitingServerData) message.", session.getGameProfile().getName()); break; case COMPLETE: if (!message.getPhase().equals(ForgeClientHandshakePhase.WAITING_SERVER_COMPLETE)) { session.disconnect( "Retrieved unexpected forge handshake ack message. (Got " + message.getPhase() + ", expected " + ForgeClientHandshakePhase.WAITING_SERVER_COMPLETE + ")"); } else { session.send(new MessageForgeHandshakeInOutAck(ForgeServerHandshakePhase.COMPLETE)); phase.set(ForgeServerHandshakePhase.DONE); } LanternGame.log() .info( "{}: Forge handshake -> Received ack (waitingServerComplete) message.", session.getGameProfile().getName()); break; case DONE: if (!message.getPhase().equals(ForgeClientHandshakePhase.PENDING_COMPLETE) && !message.getPhase().equals(ForgeClientHandshakePhase.COMPLETE)) { session.disconnect( "Retrieved unexpected forge handshake ack message. (Got " + message.getPhase() + ", expected " + ForgeClientHandshakePhase.PENDING_COMPLETE + " or " + ForgeClientHandshakePhase.COMPLETE + ")"); } else { if (message.getPhase().equals(ForgeClientHandshakePhase.PENDING_COMPLETE)) { session.send(new MessageForgeHandshakeInOutAck(ForgeServerHandshakePhase.DONE)); LanternGame.log() .info( "{}: Forge handshake -> Received ack (pendingComplete) message.", session.getGameProfile().getName()); } else { session.setProtocolState(ProtocolState.PLAY); session.spawnPlayer(); LanternGame.log() .info( "{}: Forge handshake -> Received ack (complete) message.", session.getGameProfile().getName()); } } break; case ERROR: break; default: session.disconnect( "Retrieved unexpected forge handshake ack message. (Got " + message.getPhase() + ")"); } }
@Override public void handle(NetworkContext context, MessageLoginInEncryptionResponse message) { Session session = context.getSession(); PrivateKey privateKey = session.getServer().getKeyPair().getPrivate(); // Create rsaCipher Cipher rsaCipher; try { rsaCipher = Cipher.getInstance("RSA"); } catch (GeneralSecurityException e) { LanternGame.log().error("Could not initialize RSA cipher", e); session.disconnect("Unable to initialize RSA cipher."); return; } // Decrypt shared secret SecretKey sharedSecret; try { rsaCipher.init(Cipher.DECRYPT_MODE, privateKey); sharedSecret = new SecretKeySpec(rsaCipher.doFinal(message.getSharedSecret()), "AES"); } catch (Exception e) { LanternGame.log().warn("Could not decrypt shared secret", e); session.disconnect("Unable to decrypt shared secret."); return; } // Decrypt verify token byte[] verifyToken; try { rsaCipher.init(Cipher.DECRYPT_MODE, privateKey); verifyToken = rsaCipher.doFinal(message.getVerifyToken()); } catch (Exception e) { LanternGame.log().warn("Could not decrypt verify token", e); session.disconnect("Unable to decrypt verify token."); return; } // Check verify token if (!Arrays.equals(verifyToken, session.getVerifyToken())) { session.disconnect("Invalid verify token."); return; } // Initialize stream encryption session.setEncryption(sharedSecret); // Create hash for auth String hash; try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); String sessionId = context.getChannel().attr(HandlerLoginStart.SESSION_ID).getAndRemove(); digest.update(sessionId.getBytes()); digest.update(sharedSecret.getEncoded()); digest.update(session.getServer().getKeyPair().getPublic().getEncoded()); // BigInteger takes care of sign and leading zeroes hash = new BigInteger(digest.digest()).toString(16); } catch (NoSuchAlgorithmException e) { LanternGame.log().error("Unable to generate SHA-1 digest", e); session.disconnect("Failed to hash login data."); return; } // Start auth thread Thread clientAuthThread = new Thread(new ClientAuthRunnable(session, session.getVerifyUsername(), hash)); clientAuthThread.setName("ClientAuth{" + session.getVerifyUsername() + "}"); clientAuthThread.start(); }