Advertisement
Guest User

RS2LoginProtocol

a guest
Jan 30th, 2016
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.08 KB | None | 0 0
  1. package main.astraeus.network.protocol.codec.login;
  2.  
  3. import java.math.BigInteger;
  4. import java.security.SecureRandom;
  5. import java.util.List;
  6. import java.util.Random;
  7. import java.util.logging.Logger;
  8.  
  9. import io.netty.buffer.ByteBuf;
  10. import io.netty.channel.ChannelFutureListener;
  11. import io.netty.channel.ChannelHandlerContext;
  12. import io.netty.handler.codec.ByteToMessageDecoder;
  13. import main.astraeus.network.NetworkConstants;
  14. import main.astraeus.network.packet.PacketBuilder;
  15. import main.astraeus.network.protocol.ProtocolConstants;
  16. import main.astraeus.network.protocol.codec.IsaacCipher;
  17.  
  18. /**
  19.  * The class that handles a connection through the login protocol.
  20.  *
  21.  * @author SeVen
  22.  */
  23. public final class LoginDecoder extends ByteToMessageDecoder {
  24.  
  25.     /**
  26.      * The single logger for this class.
  27.      */
  28.     private static final Logger LOGGER = Logger.getLogger(LoginDecoder.class.getName());
  29.    
  30.     /**
  31.      * Generates random numbers via secure cryptography. Generates the session key for packet
  32.      * encryption.
  33.      */
  34.     private static final Random RANDOM = new SecureRandom();
  35.  
  36.     /**
  37.      * The size of the encrypted data.
  38.      */
  39.     private int encryptedLoginBlockSize;
  40.    
  41.     /**
  42.      * The buffer that is holding the crypted data.
  43.      */
  44.     private ByteBuf rsaBuffer;
  45.    
  46.     /**
  47.      * The encryptor for outgoing data.
  48.      */
  49.     private IsaacCipher encryptor;
  50.    
  51.     /**
  52.      * The deccryptor for incoming data.
  53.      */
  54.     private IsaacCipher decryptor;  
  55.    
  56.     /**
  57.      * The current state of this connection through the login protocol.
  58.      */
  59.     private State state = State.REQUEST;
  60.  
  61.    
  62.     @Override
  63.     protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
  64.         switch (state) {
  65.        
  66.         case REQUEST:
  67.             decodeRequest(ctx, in);            
  68.             break;
  69.            
  70.         case CONNECTION_TYPE:
  71.             decodeConnectionType(ctx, in);            
  72.             break;
  73.            
  74.         case PRECRYPTED:
  75.             decodePreCrypted(in);            
  76.             break;
  77.            
  78.         case CRYPTED:
  79.             decodeCrypted(ctx, in, out);            
  80.             break;
  81.            
  82.         }
  83.     }
  84.  
  85.     /**
  86.      * The stage that decodes the request opcode, and nashHash.
  87.      *
  88.      * The request opcode, is either the game server {@code 14} or update server {@code 15}.
  89.      *
  90.      * @param ctx The context for this ChannelHandler
  91.      *
  92.      * @param in The incoming data.
  93.      */
  94.     private void decodeRequest(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
  95.         if (in.readableBytes() >= 2) {
  96.             System.out.println("decoding request");
  97.             int request = in.readUnsignedByte();          
  98.  
  99.             @SuppressWarnings("unused")
  100.             int nameHash = in.readUnsignedByte();
  101.  
  102.             if (request != ProtocolConstants.GAME_SEVER_OPCODE && request != ProtocolConstants.FILE_SERVER_OPCODE) {
  103.                 sendResponseCode(ctx, LoginResponse.INVALID_LOGIN_SERVER);
  104.                 return;
  105.             }
  106.            
  107.             ByteBuf buf = ctx.alloc().buffer(17);
  108.             buf.writeLong(0);
  109.             buf.writeByte(0);
  110.             buf.writeLong(RANDOM.nextLong());
  111.             ctx.writeAndFlush(buf);
  112.            
  113.             state = State.CONNECTION_TYPE;
  114.         }
  115.     }
  116.  
  117.     /**
  118.      * The stage that decodes the type of connection.
  119.      *
  120.      * {@code 16} denotes a new connection
  121.      *
  122.      * {@code 18} denotes a reconnection.
  123.      *
  124.      * @param in The incoming data.
  125.      */
  126.     private void decodeConnectionType(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
  127.         if (in.readableBytes() >= 2) {
  128.             System.out.println("decoding connection type");
  129.             int connectionType = in.readUnsignedByte();            
  130.  
  131.             if (connectionType != ProtocolConstants.NEW_CONNECTION_OPCODE && connectionType != ProtocolConstants.RECONNECTION_OPCODE) {
  132.                 sendResponseCode(ctx, LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
  133.                 return;
  134.             }
  135.            
  136.             state = State.PRECRYPTED;
  137.         }
  138.     }
  139.    
  140.     /**
  141.      * The stage that decodes the login block size.
  142.      *
  143.      * @param in The incoming data.
  144.      */
  145.     private void decodePreCrypted(ByteBuf in) throws Exception {
  146.         if (in.readableBytes() >= 2) {
  147.             System.out.println("decoding precrypted");
  148.            
  149.             encryptedLoginBlockSize = in.readUnsignedByte();
  150.            
  151.             if ((encryptedLoginBlockSize - 40) <= 0) {
  152.                 LOGGER.info("encryptedLoginBlockSize <= 0");
  153.                 return;
  154.             }
  155.            
  156.             state = State.CRYPTED;
  157.         }
  158.     }
  159.  
  160.     /**
  161.      * The stage that decodes the encrypted data.
  162.      *
  163.      * @param in The incoming data.
  164.      */
  165.     private void decodeCrypted(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
  166.         if (in.isReadable(encryptedLoginBlockSize)) {          
  167.             System.out.println("decoding crypted");
  168.             int magicId = in.readUnsignedByte();
  169.            
  170.             if (magicId != 255) {
  171.                 LOGGER.info("MagicId != 255");
  172.                 return;
  173.             }
  174.  
  175.             int clientVersion = in.readUnsignedShort();
  176.            
  177.             if (clientVersion != 317) {
  178.                 LOGGER.info("clientVersion != 317");
  179.                 return;
  180.             }
  181.  
  182.            int memoryVersion = in.readUnsignedByte();
  183.            
  184.             if (memoryVersion != 0 && memoryVersion != 1) {
  185.                 sendResponseCode(ctx, LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
  186.                 return;
  187.             }
  188.  
  189.             int[] crcs = new int[9];
  190.            
  191.             for (int index = 0; index < crcs.length; index++) {            
  192.                 crcs[index] = in.readInt();
  193.             }
  194.  
  195.             int expectedSize = in.readUnsignedByte();
  196.  
  197.             if (expectedSize != encryptedLoginBlockSize - 41) {
  198.                 sendResponseCode(ctx, LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
  199.                 return;
  200.             }
  201.  
  202.             byte[] rsaBytes = new byte[encryptedLoginBlockSize - 41];
  203.             in.readBytes(rsaBytes);
  204.  
  205.             rsaBuffer = ctx.alloc().buffer();
  206.             rsaBuffer.writeBytes(new BigInteger(rsaBytes).modPow(NetworkConstants.RSA_EXPONENT, NetworkConstants.RSA_MODULUS).toByteArray());
  207.  
  208.             int rsa = rsaBuffer.readUnsignedByte();            
  209.            
  210.             if (rsa != 10) {
  211.                 sendResponseCode(ctx, LoginResponse.LOGIN_SERVER_REJECTED_SESSION);
  212.                 return;
  213.             }
  214.  
  215.             long clientHalf = rsaBuffer.readLong();
  216.             long serverHalf = rsaBuffer.readLong();
  217.  
  218.             int[] isaacSeed = {
  219.                     (int) (clientHalf >> 32),
  220.                     (int)  clientHalf,
  221.                     (int) (serverHalf >> 32),
  222.                     (int) serverHalf };
  223.  
  224.             decryptor = new IsaacCipher(isaacSeed);
  225.             for (int index = 0; index < isaacSeed.length; index++) {
  226.                 isaacSeed[index] += 50;
  227.             }
  228.            
  229.             encryptor = new IsaacCipher(isaacSeed);
  230.  
  231.             System.out.println("decoding details");
  232.            
  233.             @SuppressWarnings("unused") int uid = rsaBuffer.readInt();
  234.  
  235.             PacketBuilder builder = new PacketBuilder(rsaBuffer);
  236.             String username = builder.getString();
  237.             String password = builder.getString();
  238.            
  239.             if (password.length() < 6 || password.length() > 20 || username.isEmpty() || username.length() > 12) {
  240.                 sendResponseCode(ctx, LoginResponse.INVALID_CREDENTIALS);
  241.                 return;
  242.             }
  243.            
  244.             rsaBuffer.release();
  245.  
  246.             out.add(new LoginDetailsPacket(ctx, username, password, encryptor, decryptor));
  247.         }
  248.     }
  249.    
  250.     /**
  251.      * Sends a response code to the client to notify the user logging in.
  252.      *
  253.      * @param ctx
  254.      *      The context of the channel handler.
  255.      *
  256.      * @param response
  257.      *      The response code to send.
  258.      */
  259.     private void sendResponseCode(ChannelHandlerContext ctx, LoginResponse response) {
  260.         ByteBuf buffer = ctx.alloc().buffer(Byte.BYTES);
  261.         buffer.writeByte(response.getOpcode());
  262.         ctx.writeAndFlush(buffer).addListener(ChannelFutureListener.CLOSE);
  263.     }
  264.    
  265.     /**
  266.      * Represents the enumerated states of the login protocol
  267.      *
  268.      * @author SeVen
  269.      */
  270.     private enum State {
  271.        
  272.         /**
  273.          * The state where a login server is selected.
  274.          * <p>
  275.          * Game Server : 14
  276.          * <p>
  277.          * File Server : 15
  278.          * <p>
  279.          */
  280.         REQUEST,
  281.        
  282.         /**
  283.          * The state that where the type of login connection is indicated.
  284.          * <p>
  285.          * New Connection : 16
  286.          * <p>
  287.          * Reconnecting : 18
  288.          * <p>
  289.          */
  290.         CONNECTION_TYPE,
  291.        
  292.         /**
  293.          * The state that determines the size of the encrypted data.
  294.          */
  295.         PRECRYPTED,
  296.        
  297.         /**
  298.          * The state that decodes the encrypted data.
  299.          */
  300.         CRYPTED;
  301.     }
  302.    
  303.  
  304. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement