goauth

Fabric plugin for enhanced whitelists
git clone git://git.bain.cz/goauth.git
Log | Files | Refs | README | LICENSE

WhitelistUpdateRunner.java (5212B)


      1 package cz.bain.plugins.goauth;
      2 
      3 import com.google.gson.JsonArray;
      4 import com.google.gson.JsonElement;
      5 import com.google.gson.JsonObject;
      6 import com.google.gson.JsonParser;
      7 
      8 import javax.crypto.Cipher;
      9 import javax.crypto.spec.IvParameterSpec;
     10 import javax.crypto.spec.SecretKeySpec;
     11 import java.io.IOException;
     12 import java.io.InputStream;
     13 import java.io.OutputStream;
     14 import java.net.InetSocketAddress;
     15 import java.net.ServerSocket;
     16 import java.net.Socket;
     17 import java.net.SocketTimeoutException;
     18 import java.nio.charset.StandardCharsets;
     19 import java.security.GeneralSecurityException;
     20 
     21 public class WhitelistUpdateRunner implements Runnable {
     22 
     23     private final SecretKeySpec key;
     24     private final Goauth plugin;
     25 
     26     public WhitelistUpdateRunner(byte[] key, Goauth plugin) {
     27         this.key = new SecretKeySpec(key, "ChaCha20");
     28         this.plugin = plugin;
     29     }
     30 
     31     @Override
     32     public void run() {
     33         ServerSocket serverSocket;
     34         try {
     35             serverSocket = new ServerSocket();
     36             serverSocket.bind(new InetSocketAddress(Goauth.port), 2);
     37             serverSocket.setSoTimeout(5);
     38 
     39         } catch (IOException e) {
     40             Goauth.LOGGER.error("Failed to create socket for updates!");
     41             return;
     42         }
     43         while (this.plugin.running) {
     44             try (
     45                     Socket socket = serverSocket.accept();
     46                     InputStream inputStream = socket.getInputStream();
     47                     OutputStream outputStream = socket.getOutputStream()
     48             ) {
     49                 // protecting ourselves from being bombarded with large amounts of data
     50                 byte[] sizeBytes = inputStream.readNBytes(4);
     51                 if (sizeBytes.length != 4) continue;
     52                 int size = fromByteArray(sizeBytes);
     53                 if (size > 2048 || size <= 0) {
     54                     outputStream.write(0x01);
     55                     outputStream.flush();
     56                     Goauth.LOGGER.warn("Whitelist update rejected: payload size too big ({})", size);
     57                     continue;
     58                 } else {
     59                     outputStream.write(0x00);
     60                     outputStream.flush();
     61                 }
     62                 byte[] iv = inputStream.readNBytes(12);
     63                 if (iv.length != 12) continue;
     64                 byte[] cipherText = inputStream.readNBytes(size);
     65                 if (cipherText.length != size) continue;
     66                 byte[] plainText;
     67                 try {
     68                     plainText = decryptMessage(cipherText, iv);
     69                 } catch (GeneralSecurityException e) {
     70                     Goauth.LOGGER.warn("Whitelist update rejected: failed to decrypt payload");
     71                     outputStream.write(0x01);
     72                     outputStream.flush();
     73                     continue;
     74                 }
     75                 JsonParser parser = new JsonParser();
     76                 JsonArray el = parser.parse(new String(plainText, StandardCharsets.UTF_8)).getAsJsonArray();
     77                 Goauth.LOGGER.info("Got " + el.size() + " new whitelist updates");
     78                 synchronized (this.plugin.completed) {
     79                     for (JsonElement user : el) {
     80                         JsonObject userObject = user.getAsJsonObject();
     81                         String username = userObject.get("username").getAsString();
     82                         OAuthUpdate.UpdateType type = OAuthUpdate.UpdateType.fromString(userObject.get("type").getAsString());
     83                         if (type == null) {
     84                             Goauth.LOGGER.warn("Whitelist update rejected: bad update data (type={})", userObject.get("type").getAsString());
     85                             outputStream.write(0x01);
     86                             outputStream.flush();
     87                             break;
     88                         }
     89                         this.plugin.completed.add(new OAuthUpdate(type, username));
     90                         outputStream.write(0x00);
     91                     }
     92                 }
     93             } catch (SocketTimeoutException ignored) {
     94             } catch (IOException | IllegalStateException e) {
     95                 Goauth.LOGGER.warn("Error while processing OAuth updates, dropping connection");
     96                 e.printStackTrace();
     97             }
     98         }
     99     }
    100 
    101     /**
    102      * Decrypts a message with the ChaCha20-Poly1305 cipher.
    103      *
    104      * @param cipherText cipher text with appended digest to the end
    105      * @param iv         unique nonce
    106      * @return plain text
    107      * @throws GeneralSecurityException when the decryption goes wrong
    108      */
    109     private byte[] decryptMessage(byte[] cipherText, byte[] iv) throws GeneralSecurityException {
    110         Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305");
    111         IvParameterSpec ivSpec = new IvParameterSpec(iv);
    112         cipher.init(Cipher.DECRYPT_MODE, this.key, ivSpec);
    113 
    114         return cipher.doFinal(cipherText);
    115     }
    116 
    117     /**
    118      * Little endian int from byte array
    119      *
    120      * @param bytes byte array
    121      * @return int
    122      */
    123     private int fromByteArray(byte[] bytes) {
    124         return ((bytes[0] & 0xFF) << 24) |
    125                 ((bytes[1] & 0xFF) << 16) |
    126                 ((bytes[2] & 0xFF) << 8) |
    127                 (bytes[3] & 0xFF);
    128     }
    129 }