From cf5d1b61c750ac9ab3442df1d3fc6251504a769d Mon Sep 17 00:00:00 2001 From: Quackster Date: Sat, 29 Jul 2023 11:33:09 +1000 Subject: [PATCH] Add two-way client/server encryption --- .../main/java/org/alexdev/havana/Havana.java | 6 + .../havana/game/encryption/Cryptography.java | 178 +++++++ .../havana/game/encryption/DiffieHellman.java | 81 ++-- .../havana/game/encryption/HugeInt15.java | 454 ++++++++++++++++++ .../havana/game/encryption/SecurityCode.java | 72 +-- .../alexdev/havana/game/player/Player.java | 40 +- .../incoming/handshake/GENERATEKEY.java | 15 +- .../incoming/handshake/INIT_CRYPTO.java | 31 +- .../outgoing/handshake/CRYPTO_PARAMETERS.java | 9 +- .../server/netty/NettyChannelInitializer.java | 4 +- .../server/netty/NettyPlayerNetwork.java | 101 +++- .../server/netty/codec/EncryptionDecoder.java | 66 ++- .../server/netty/codec/EncryptionEncoder.java | 61 +++ .../server/netty/codec/NetworkDecoder.java | 4 +- .../server/netty/codec/NetworkEncoder.java | 61 +-- 15 files changed, 976 insertions(+), 207 deletions(-) create mode 100644 Havana-Server/src/main/java/org/alexdev/havana/game/encryption/Cryptography.java create mode 100644 Havana-Server/src/main/java/org/alexdev/havana/game/encryption/HugeInt15.java create mode 100644 Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionEncoder.java diff --git a/Havana-Server/src/main/java/org/alexdev/havana/Havana.java b/Havana-Server/src/main/java/org/alexdev/havana/Havana.java index d7b41c9..84ca949 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/Havana.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/Havana.java @@ -12,6 +12,8 @@ import org.alexdev.havana.game.catalogue.collectables.CollectablesManager; import org.alexdev.havana.game.commands.CommandManager; import org.alexdev.havana.game.ecotron.EcotronManager; import org.alexdev.havana.game.effects.EffectsManager; +import org.alexdev.havana.game.encryption.Cryptography; +import org.alexdev.havana.game.encryption.HugeInt15; import org.alexdev.havana.game.events.EventsManager; import org.alexdev.havana.game.fuserights.FuserightsManager; import org.alexdev.havana.game.games.GameManager; @@ -29,9 +31,11 @@ import org.alexdev.havana.game.texts.TextsManager; import org.alexdev.havana.game.wordfilter.WordfilterManager; import org.alexdev.havana.messages.MessageHandler; import org.alexdev.havana.server.mus.MusServer; +import org.alexdev.havana.server.netty.NettyPlayerNetwork; import org.alexdev.havana.server.netty.NettyServer; import org.alexdev.havana.server.rcon.RconServer; import org.alexdev.havana.util.DateUtil; +import org.alexdev.havana.util.StringUtil; import org.alexdev.havana.util.config.GameConfiguration; import org.alexdev.havana.util.config.LoggingConfiguration; import org.alexdev.havana.util.config.ServerConfiguration; @@ -42,9 +46,11 @@ import org.slf4j.LoggerFactory; import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import java.io.IOException; +import java.math.BigInteger; import java.net.UnknownHostException; import java.time.LocalTime; import java.time.format.DateTimeFormatter; +import java.util.Arrays; import java.util.Calendar; import java.util.Locale; diff --git a/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/Cryptography.java b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/Cryptography.java new file mode 100644 index 0000000..e8a07d5 --- /dev/null +++ b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/Cryptography.java @@ -0,0 +1,178 @@ +package org.alexdev.havana.game.encryption; + +import java.util.ArrayList; +import java.util.HashMap; + +public class Cryptography { + private int[] pR3hu24v5; + private int[] artificialKey; + private int[] tKey; + private int j, i, q; + private String pTableStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + private HashMap pTableList = new HashMap<>(); + + public Cryptography(int[] tMyKey) { + //String tMyKeyS = String.valueOf(tMyKey); + this.pR3hu24v5 = new int[256]; + this.tKey = new int[256]; + this.artificialKey = new int[]{204, 53, 74, 109, 63, 4, 163, 182, 210, 186, 19, 162, 160, 115, 139, 83, 235, 177, 14, 15, 11, 127, 4, 210, 222, 138, 10, 138, 151, 236, 158, 186, 67, 1, 168, 69, 139, 214, 243, 32, 157, 161, 211, 155, 20, 192, 214, 155, 12, 153, 192, 112, 98, 146, 33, 30, 22, 131, 81, 161, 105, 142, 103, 204, 112, 9, 167, 185, 176, 51, 27, 166, 249, 228, 24, 165, 197, 25, 166, 216, 74, 14, 104, 15, 77, 49, 6, 50, 65, 126, 10, 187, 15, 17, 189, 155, 246, 221, 92, 104, 79, 87, 186, 88, 80, 50, 223, 126, 148, 217, 81, 223, 91, 70, 165, 237, 150, 95, 195, 205, 199, 176, 156, 122, 187, 232, 252, 230, 169, 94, 157, 194, 44, 164, 208, 22, 141, 139, 167, 236, 201, 42, 130, 14, 44, 57, 253, 224, 130, 118, 242, 226, 146, 202, 154, 40, 201, 171, 160, 91, 143, 144, 150, 197, 169, 204, 121, 131, 139, 112, 214, 196, 74, 123, 159, 220, 77, 176, 151, 73, 125, 135, 166, 26, 176, 31, 255, 234, 91, 30, 218, 41, 121, 17, 45, 3, 234, 35, 185, 52, 112, 108, 65, 72, 184, 93, 225, 113, 62, 0, 110, 38, 43, 15, 44, 114, 162, 167, 69, 40, 103, 144, 114, 215, 228, 47, 112, 235, 179, 211, 116, 237, 70, 167, 36, 224, 183, 11, 0, 74, 145, 241, 153, 40, 151, 211, 231, 199, 235, 176, 109, 95, 160, 141, 137, 236, 39, 17, 246, 97, 120, 227, 12, 1, 195, 239, 150, 169, 85, 226, 23, 58, 145, 157, 37, 218, 132, 168, 94, 15, 240, 24, 152, 230, 249, 80, 145, 208, 209, 144, 154, 228, 197, 40, 6, 248, 90, 15, 1, 82, 145, 77, 220, 27, 167, 0, 149, 0, 103, 53, 226, 242, 175, 9, 177, 130, 65, 216, 107, 4, 194, 71, 135, 231, 151, 178, 188, 220, 33, 152, 120, 165, 73, 124, 32, 215, 127, 130, 29, 40, 20, 3, 212, 254, 106, 42, 98, 7, 8, 129, 195, 30, 74, 118, 169, 81, 88, 235, 149, 232, 181, 182, 206, 82, 163, 26, 116, 37, 41, 50, 63, 185, 165, 2, 81, 10, 149, 103, 211, 168, 34, 55, 32, 233, 16, 238, 219, 235, 170, 255, 244, 12, 89, 211, 88, 33, 24, 38, 190, 75, 70, 86, 89, 2, 189, 134, 207, 65, 6, 148, 124, 22, 57, 21, 118, 227, 173, 21, 236, 236, 139, 189, 230, 153, 153, 182, 230, 216, 26, 0, 9, 50, 32, 189, 97, 3, 208, 201, 103, 163, 96, 0, 42, 11, 173, 98, 102, 76, 31, 243, 59, 71, 223, 252, 186, 157, 231, 90, 212, 83, 10, 69, 69, 165, 209, 112, 157, 237, 24, 90, 4, 44, 247, 32, 159, 126, 171, 99, 216, 196, 228, 217, 157, 143, 32, 16, 111, 67, 106, 231, 10, 167, 13, 240, 182, 105, 52, 12, 84, 91, 243, 205, 180, 180, 35, 58, 238, 240, 0, 209, 48, 249, 243, 209, 93, 10, 22, 183, 5, 177, 110, 16, 188, 201, 240, 194, 11, 76, 219, 67, 254, 176, 139, 66, 81, 138, 109, 178, 71, 143, 74, 217, 52, 0, 127, 190, 12, 214, 231, 84, 239, 165, 155, 89, 95, 106, 62, 30, 182, 137, 85, 39, 221, 51, 188, 149, 104, 167, 71, 11, 220, 212, 246, 114, 10, 4, 216, 127, 233, 231, 178, 174, 181, 29, 49, 118, 177, 108, 156, 174, 118, 196, 216, 106, 203, 96, 65, 12, 140, 248, 152, 35, 152, 17, 89, 136, 138, 94, 5, 190, 92, 189, 16, 216, 61, 70, 165, 36, 238, 167, 16, 61, 206, 140, 226, 251, 37, 225, 211, 111, 42, 195, 36, 248, 233, 67, 146, 100, 244, 23, 154, 103, 48, 4, 15, 33, 169, 151, 13, 151, 115, 173, 37, 103, 172, 23, 182, 29, 22, 25, 54, 46, 188, 14, 24, 12, 182, 241, 163, 90, 121, 172, 29, 73, 191, 91, 232, 229, 197, 200, 32, 7, 67, 214, 141, 248, 10, 135, 168, 4, 144, 17, 94, 228, 76, 202, 130, 174, 251, 170, 100, 173, 232, 183, 132, 130, 35, 163, 1, 154, 134, 56, 202, 13, 190, 224, 56, 107, 107, 244, 16, 12, 149, 220, 120, 245, 179, 103, 85, 255, 195, 187, 191, 82, 225, 13, 206, 106, 60, 212, 12, 211, 247, 112, 185, 5, 56, 226, 236, 179, 181, 208, 204, 16, 159, 158, 36, 65, 101, 148, 23, 89, 125, 27, 61, 117, 255, 142, 32, 138, 105, 166, 203, 253, 113, 138, 30, 247, 250, 198, 21, 244, 113, 40, 161, 229, 179, 100, 76, 30, 177, 69, 87, 90, 9, 135, 254, 108, 99, 145, 195, 145, 138, 223, 237, 52, 126, 244, 109, 171, 44, 0, 187, 129, 127, 49, 220, 100, 253, 0, 116, 93, 87, 39, 245, 5, 54, 203, 241, 155, 255, 125, 80, 253, 75, 71, 242, 147, 153, 148, 214, 91, 33, 181, 78, 10, 82, 171, 89, 179, 221, 144, 224, 138, 112, 254, 152, 186, 190, 224, 44, 251, 60, 133, 65, 70, 72, 203, 126, 123, 212, 108, 68, 185, 42, 208, 51, 11, 177, 3, 24, 207, 14, 148, 113, 55, 1, 19, 179, 31, 133, 11, 227, 72, 145, 242, 157, 244, 239, 129, 124, 109, 56, 134, 56, 95, 110, 161, 73, 151, 136, 67, 176, 201, 193, 70, 53, 31, 238, 84, 81, 65, 50, 182, 20, 17, 247, 179, 217, 14, 34, 182, 97, 55, 117, 176, 108, 234, 147, 89, 168, 7, 251, 212, 22, 107, 63, 248, 179, 222, 167, 214, 136, 74, 53, 47, 120, 233, 131, 41, 167, 220, 56, 12, 51, 125, 207, 112, 179, 211, 47, 134, 223, 112, 223, 46, 249, 24, 64, 58, 36, 187, 77, 132, 116, 116, 111, 36, 127, 217, 177, 24, 58, 102, 166, 105, 119, 234, 187, 198, 77, 153, 23, 157, 103, 92, 33, 136, 182, 131, 154, 141, 149, 4, 117, 213, 226, 64, 116, 55, 6, 159, 126, 225}; + + for (int k = 0; k < this.pTableStr.length(); k++) { + this.pTableList.put((int) this.pTableStr.charAt(k), k); + } + + int[] tXorVals = new int[] { + 109, 87, 120, 70, 82, 74, 110, 71, 74, 53, 84, 57, 83, 105, 48, 79, 77, 86, 118, 69, 66, 66, 109, 56, 108, 97, 105, 104, 88, 107, 78, 56, 71, 109, 72, 54, 102, 117, 118, 55, 108, 100, 90, 104, 76, 121, 71, 82, 82, 75, 67, 99, 71, 122, 122, 105, 80, 89, 66, 97, 74, 111, 109 + }; + + ArrayList tModKey = new ArrayList<>(); + int l = 0; + for (int value : tMyKey) { + int tVal = value ^ tXorVals[l]; + tModKey.add(tVal); + l = (l + 1) % tXorVals.length; + } + for (int q = 0; q < 256; q++) { + tKey[q] = tModKey.get(q % tModKey.size()); + pR3hu24v5[q] = q; + } + + j = 0; + for (int q = 0; q < 256; q++) { + j = (j + pR3hu24v5[q] + tKey[q]) % 256; + int k = pR3hu24v5[q]; + pR3hu24v5[q] = pR3hu24v5[j]; + pR3hu24v5[j] = k; + } + + this.q = 0; + this.j = 0; + this.i = 0; + String tPrMixString = "NV6VVFPoC7FLDlzDUri3qcOAg9cRoFOmsYR9ffDGy5P8HfF6eekX40SFSVfJ1mDb3lcpYRqdg28sp61eHkPukKbqTu1JsVEKiRavi04YtSzUsLXaYSa5BEGwg5G2OF"; + for (l = 1; l <= 52; l++) { + this.AkwGx8bHG2kc1xGG4xbdHPCV0fqvK(tPrMixString); + } + } + + public String AkwGx8bHG2kc1xGG4xbdHPCV0fqvK(String tdata) { + int[] tBytes = new int[tdata.length()]; + for (int e = 0; e < tdata.length(); e++) { + int a = (int) tdata.charAt(e); + if (a > 255) { + tBytes[e] = (a - (a % 256)) / 256; + if (a % 256 != 0) { + tBytes[e] = a % 256; + } + } else { + tBytes[e] = a; + } + } + + int[] tDataOut = new int[tBytes.length]; + for (int a = 0; a < tBytes.length; a++) { + q = (q + 1) % 256; + j = (j + pR3hu24v5[q]) % 256; + int temp = pR3hu24v5[q]; + pR3hu24v5[q] = pR3hu24v5[j]; + pR3hu24v5[j] = temp; + if ((q & 63) == 63) { + int tI = 297 * (q + 67) % 256; + int tJ = (j + pR3hu24v5[tI]) % 256; + temp = pR3hu24v5[tI]; + pR3hu24v5[tI] = pR3hu24v5[tJ]; + pR3hu24v5[tJ] = temp; + } + int d = pR3hu24v5[((pR3hu24v5[q] + pR3hu24v5[j]) % 256)]; + int tResult = tBytes[a] ^ d; + tDataOut[a] = tResult; + } + + StringBuilder tCipher = new StringBuilder(); + int a = 1; + while (a <= tDataOut.length) { + int tNum1 = tDataOut[(a - 1)]; + int tNum2 = (a < tDataOut.length) ? tDataOut[(a)] : 0; + int tNum3 = (a + 1 < tDataOut.length) ? tDataOut[(a + 1)] : 0; + int tByte1 = (tNum1 & 252) / 4; + int tByte2a = (tNum1 & 3) * 16; + int tByte2b = (tNum2 & 240) / 16; + int tByte2 = tByte2a | tByte2b; + tCipher.append(pTableStr.charAt(tByte1)).append(pTableStr.charAt(tByte2)); + + if (tDataOut.length > a) { + int tByte3a = (tNum2 & 15) * 4; + int tByte3b = (tNum3 & 192) / 64; + int tByte3 = tByte3a | tByte3b; + tCipher.append(pTableStr.charAt(tByte3)); + } + + if (tDataOut.length > a + 1) { + int tByte4 = tNum3 & 63; + tCipher.append(pTableStr.charAt(tByte4)); + } + a += 3; + } + int i = (int) (Math.random() * 256) - 1; + return tCipher.toString(); + } + + public String kg4R6Jo5xjlqtFGs1klMrK4ZTzb3R(String tdata) { + StringBuilder tCipher = new StringBuilder(); + int a = 0; + while (a < tdata.length()) { + ArrayList tDataIn = new ArrayList(); + int tNum1 = pTableList.get((int)tdata.charAt(a)); + int tNum2 = pTableList.get((int)tdata.charAt(a + 1)); + if (tNum2 < 0) { + tNum2 = 0; + } + int tByte1a = tNum1 * 4; + int tByte1b = (tNum2 & 48) / 16; + int tByte1 = tByte1a | tByte1b; + tDataIn.add(tByte1); + int tNum3 = 0; + if (tdata.length() > (a + 2)) { + tNum3 = pTableList.get((int)tdata.charAt(a + 2)); + if (tNum3 < 0) { + tNum3 = 0; + } + int tByte2a = (tNum2 & 15) * 16; + int tByte2b = (tNum3 & 60) / 4; + int tByte2 = tByte2a | tByte2b; + tDataIn.add(tByte2); + } + if (tdata.length() > (a + 3)) { + int tNum4 = pTableList.get((int)tdata.charAt(a + 3)); + if (tNum4 < 0) { + tNum4 = 0; + } + int tByte3a = (tNum3 & 3) * 64; + int tByte3b = tNum4 & 63; + int tByte3 = tByte3a | tByte3b; + tDataIn.add(tByte3); + } + a = a + 4; + for (int k = 0; k < tDataIn.size(); k++) { + q = (q + 1) % 256; + j = (j + pR3hu24v5[q]) % 256; + int temp = pR3hu24v5[q]; + pR3hu24v5[q] = pR3hu24v5[j]; + pR3hu24v5[j] = temp; + if ((q & 63) == 63) { + int tI = 297 * (q + 67) % 256; + int tJ = (j + pR3hu24v5[tI]) % 256; + temp = pR3hu24v5[tI]; + pR3hu24v5[tI] = pR3hu24v5[tJ]; + pR3hu24v5[tJ] = temp; + } + int d = pR3hu24v5[((pR3hu24v5[q] + pR3hu24v5[j]) % 256) ]; + //System.out.println (tDataIn.get(k) + " / " + d); + tCipher.append((char) ((tDataIn.get(k) ^ d))); + + } + } + + return tCipher.toString(); + } + +} \ No newline at end of file diff --git a/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/DiffieHellman.java b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/DiffieHellman.java index 8359554..bb49be1 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/DiffieHellman.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/DiffieHellman.java @@ -2,84 +2,73 @@ package org.alexdev.havana.game.encryption; import java.math.BigInteger; import java.util.concurrent.ThreadLocalRandom; +import java.security.SecureRandom; public class DiffieHellman { - private String clientPrivateKey; + private static final int BITLENGTH = 64; + private BigInteger clientP; + private BigInteger clientG; private BigInteger publicKey; private BigInteger privateKey; private BigInteger sharedKey; - private BigInteger clientP; - private BigInteger clientG; - private String clientPublicKey; public DiffieHellman() { - this.clientPrivateKey = DiffieHellman.generateRandomNumString(64); - this.privateKey = new BigInteger(this.clientPrivateKey); - this.clientP = new BigInteger(SecurityCode.assign(SecurityCode.getLoginParameter("p"))); - this.clientG = new BigInteger(SecurityCode.assign(SecurityCode.getLoginParameter("g"))); + this.privateKey = generatePrivateKey(); + + var adobeClientG = new HugeInt15(); + var adobeClientP = new HugeInt15(); + + adobeClientG.assign(SecurityCode.getLoginParameter("g"), null, true); + adobeClientP.assign(SecurityCode.getLoginParameter("p"), null, true); + + this.clientG = new BigInteger(adobeClientG.getString()); + this.clientP = new BigInteger(adobeClientP.getString()); + + this.publicKey = this.computePublicKey(this.privateKey); } - /** - * Generate shared key. - * - * @param publicKey the ckey - */ - public void generateSharedKey(String publicKey) { - this.clientPublicKey = publicKey; - this.publicKey = new BigInteger(publicKey); - this.sharedKey = this.publicKey.modPow(this.privateKey, this.clientP); + private static BigInteger generatePrivateKey() { + SecureRandom random = new SecureRandom(); + BigInteger privateKey; + do { + privateKey = new BigInteger(BITLENGTH, random); + } while (privateKey.compareTo(BigInteger.ZERO) == 0); + return privateKey; } + private BigInteger computePublicKey(BigInteger privateKey) { + return this.clientG.modPow(privateKey, this.clientP); + } - public static String generateRandomHexString(int len) { - StringBuilder result = new StringBuilder(); + private BigInteger computeSharedSecret(BigInteger privateKey, BigInteger publicKey) { + return publicKey.modPow(privateKey, this.clientP); + } - for (int i = 0; i < len; i++) { - int rand = 1 + (int) (ThreadLocalRandom.current().nextDouble() * 254); // 1 - 255 - result.append(Integer.toString(rand, 16)); - } - return result.toString(); + public void generateSharedKey(String publicServerKey) { + this.sharedKey = computeSharedSecret(this.privateKey, new BigInteger(publicServerKey)); } public static String generateRandomNumString(int len) { - int rand = 0; StringBuilder result = new StringBuilder(); char[] numbers = new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; for (int i = 0; i < len; i++) { - result.append(Character.toString(numbers[ThreadLocalRandom.current().nextInt(numbers.length)])); + result.append(numbers[ThreadLocalRandom.current().nextInt(numbers.length)]); } return result.toString(); } - - public BigInteger getClientP() { - return clientP; - } - - public BigInteger getClientG() { - return clientG; + public BigInteger getPublicKey() { + return publicKey; } public BigInteger getPrivateKey() { - return this.privateKey; - } - - public BigInteger getPublicKey() { - return this.publicKey; + return privateKey; } public BigInteger getSharedKey() { return sharedKey; } - - public String getClientPublicKey() { - return clientPublicKey; - } - - public String getClientPrivateKey() { - return clientPrivateKey; - } } diff --git a/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/HugeInt15.java b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/HugeInt15.java new file mode 100644 index 0000000..815021d --- /dev/null +++ b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/HugeInt15.java @@ -0,0 +1,454 @@ +package org.alexdev.havana.game.encryption; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HugeInt15 { + public List pData_NxIhNARqldyJyY2PfT03dK8t9OLUR; + private boolean pNegative; + private int pBase; + private int pDigits; + + public HugeInt15() { + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR = new ArrayList(); + pNegative = false; + pBase = 10000; + pDigits = Integer.toString(pBase).length() - 1; + } + + public void neg() { + if (pNegative) { + pNegative = false; + } else { + pNegative = true; + } + } + + public void assign(Object tdata, Object tLimit, boolean tUseKey) { + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR = new ArrayList(); + if (tdata instanceof String dataString) { + if (dataString.charAt(0) == '-') { + pNegative = true; + dataString = dataString.substring(1); + } else { + pNegative = false; + } + int i = dataString.length(); + while (i > 0) { + String tCoef = dataString.substring(Math.max(0, i - pDigits), i); + i = i - tCoef.length(); + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.add(Integer.valueOf(tCoef)); + } + } else if (tdata instanceof int[] tIntArray) { + List dataList = new ArrayList<>(); + + for (int j : tIntArray) { + dataList.add(j); + } + + pNegative = false; + int tZeroes = 1; + int limit = (tLimit == null) ? dataList.size() : Math.min((int) tLimit, dataList.size()); + + for (int i = 0; i < limit; i++) { + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.add(null); + } + + for (int i = 1; i <= limit; i++) { + if (dataList.get(i - 1) != 0 || tZeroes == 0) { + if (!tUseKey) { + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.set(limit - i, dataList.get(i - 1)); + } else { + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.set(limit - i, SecurityCode.decode(dataList.get(i - 1))); + } + tZeroes = 0; + } + } + } + + //System.out.println("pData_NxIhNARqldyJyY2PfT03dK8t9OLUR - " + String.join(",", pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.stream().map(x -> x == null ? "" : x.toString()).toArray(String[]::new))); + } + + public int getIntValue(Object tLimit) { + int limit = (tLimit == null) ? 100000000 : (int) tLimit; + int tLimitLo = limit / pBase * 10; + int tLength = pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size(); + int tInt = pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(tLength - 1); + int tIndex = tLength - 2; + while (tInt < limit && tIndex >= 0) { + int tCoef = pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(tIndex); + if (tInt < tLimitLo) { + tInt = (tInt * pBase) + tCoef; + } else { + int tCoefMultiplier = 10; + while ((tInt * tCoefMultiplier) < limit) { + tCoefMultiplier = tCoefMultiplier * 10; + } + int tCoefDivider = pBase / tCoefMultiplier; + tInt = (tInt * tCoefMultiplier) + (tCoef / tCoefDivider); + } + tIndex--; + } + return tInt; + } + + public String getString() { + if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() == 0) { + return "0"; + } + String tStr = ""; + for (int i = 0; i < pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size(); i++) { + String tValue = String.valueOf(pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(i)); + if (i < pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() - 1) { + while (tValue.length() < pDigits) { + tValue = "0" + tValue; + } + } + tStr = tValue + tStr; + } + if (pNegative) { + tStr = "-" + tStr; + } + return tStr; + } + + public void copyFrom(HugeInt15 tValue) { + pNegative = tValue.pNegative; + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR = new ArrayList<>(tValue.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR); + trim(); + } + + public void trim() { + for (int i = pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() - 1; i >= 0; i--) { + if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(i) == 0) { + pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.remove(i); + } else { + break; + } + } + if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.isEmpty()) { + pNegative = false; + } + } + + public int[] getIntArray(boolean tUseKey) { + ArrayList tData = new ArrayList<>(); + for (int i = 0; i < pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size(); i++) { + int tVal = pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() - (i + 1)); + if (tUseKey) { + tVal = encode(tVal); + } + tData.add(tVal); + } + int[] tArray = new int[tData.size()]; + for (int i = 0; i < tData.size(); i++) { + tArray[i] = tData.get(i); + } + return tArray; + } + + private int encode(int value) { + return (value < 0) ? (value + 256) : value; + } + + + public static int[] getByteArray(BigInteger tSharedKey) { + byte[] arr = tSharedKey.toByteArray(); + + // Remove the "sign bit" at the start that Java adds to the BigInteger so that + // it can match Shockwave's output + if (arr[0] == 0) { + byte[] bytesWithoutSignBit = new byte[arr.length - 1]; + System.arraycopy(arr, 1, bytesWithoutSignBit, 0, bytesWithoutSignBit.length); + arr = bytesWithoutSignBit; + } + + int[] result = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + if (arr[i] < 0) { + result[i] = 256 - Math.abs(arr[i]); + } else { + result[i] = arr[i]; + } + } + return result; + } + + public boolean lessThan(HugeInt15 tValue, boolean tUseSign) { + if (equals(tValue)) { + return false; + } + return !greaterThan(tValue, tUseSign); + } + + public boolean greaterThan(HugeInt15 tValue, Boolean tUseSign) { + if (equals(tValue)) { + return false; + } + if (tUseSign == null) { + tUseSign = true; + } + if (tUseSign) { + if (!pNegative && tValue.pNegative) { + return true; + } + if (pNegative && !tValue.pNegative) { + return false; + } + } + if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() > tValue.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size()) { + return tUseSign ? !pNegative : true; + } else if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() < tValue.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size()) { + return tUseSign ? pNegative : false; + } + for (int i = pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() - 1; i >= 0; i--) { + if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(i) > tValue.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(i)) { + return tUseSign ? !pNegative : true; + } else if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(i) < tValue.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(i)) { + return tUseSign ? pNegative : false; + } + } + return false; + } + + + public boolean isZero() { + for (int i = 0; i < pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size(); i++) { + if (pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(i) != 0) { + return false; + } + } + return true; + } + +/* + public HugeInt15 div(HugeInt15 tDivider, boolean tReturnModulo, boolean tKeepResult) { + if (tDivider.isZero()) { + return null; + } + HugeInt15 tResult = new HugeInt15(); + if (lessThan(tDivider, false)) { + if (tReturnModulo) { + tResult.copyFrom(this); + } + return tResult; + } + HugeInt15 tTemp = new HugeInt15(); + HugeInt15 tTemp2 = new HugeInt15(); + HugeInt15 tRemainder = new HugeInt15(); + HugeInt15 tRemainderTemp = new HugeInt15(); + tRemainder.copyFrom(this); + int tDividerInt = tDivider.getIntValue(10000); + List tResultData = new ArrayList(); + int tNegResult = 0; + if (tRemainder.pNegative) { + tRemainder.neg(); + tNegResult = 1; + } + if (tDivider.pNegative) { + tDivider.neg(); + tNegResult = tNegResult ^ 1; + } + int tDividerDigits = tDivider.getLength(); + int tDividerIntLength = getIntLength(tDividerInt); + while (!tRemainder.lessThan(tDivider)) { + int tRemainderInt = tRemainder.getIntValue(); + int tRemainderDigits = tRemainder.getLength(); + int tRemainderIntLength = getIntLength(tRemainderInt); + int tRemainderIntFirstDigits = tRemainderInt; + for (int i = tRemainderIntLength - tDividerIntLength; i >= 1; i--) { + tRemainderIntFirstDigits = tRemainderIntFirstDigits / 10; + } + int tFastCoef; + if (tRemainderIntFirstDigits != tDividerInt) { + tFastCoef = tRemainderInt / tDividerInt; + } else { + String tRemainderStr = tRemainder.getString().substring(0, tDividerDigits); + tRemainderTemp.assign(tRemainderStr, null, false); + if (tDivider.greaterThan(tRemainderTemp)) { + tFastCoef = (tRemainderInt / tDividerInt) - 1; + } else { + tFastCoef = tRemainderInt / tDividerInt; + } + } + int tDigitDelta = tRemainderDigits - tDividerDigits; + int tDigitCount = tDigitDelta % pDigits; + int tFastCoefLength = getIntLength(tFastCoef); + if ((tFastCoefLength + tDividerIntLength) > tRemainderIntLength) { + tDigitCount = tDigitCount + 1; + } + if (tDigitCount == 0) { + tDigitCount = pDigits; + } + for (int i = tFastCoefLength - tDigitCount; i >= 1; i--) { + tFastCoef = tFastCoef / 10; + } + tTemp.copyFrom(tDivider); + tTemp.mul(tFastCoef); + int tAddCount = tRemainder.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() - tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size(); + for (int i = 1; i <= tAddCount; i++) { + tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.add(0); + } + int tAddCountNew = tAddCount; + while (true) { + int tFastCoef = tRemainderDigit / tDividerDigit; + tTemp2.copyFrom(tDivider); + tTemp2.mul(tFastCoef); + int tDividerIntLength = getIntLength(tDividerDigit); + int tFastCoefLength = getIntLength(tFastCoef); + int tRemainderIntLength = getIntLength(tRemainderDigit); + int tDividerDigits = getDigits(tDividerDigit); + int tRemainderDigits = getDigits(tRemainderDigit); + int tDigitDelta = tRemainderDigits - tDividerDigits; + int tDigitCount = tDigitDelta % pDigits; + if ((tFastCoefLength + tDividerIntLength) > tRemainderIntLength) { + tDigitCount = tDigitCount + 1; + } + if (tDigitCount == 0) { + tDigitCount = pDigits; + } + if (tDigitCount > tDigitDelta) { + tDigitCount = tDigitCount - pDigits; + } + int tValidValue = 0; + tTemp.copyFrom(tDivider); + tTemp.mul(tFastCoef); + for (int i = 1; i <= tDigitCount; i++) { + tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.addAt(1, 0); + if (tTemp.greaterThan(tRemainder)) { + tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.removeAt(1); + tValidValue = 1; + next repeat; + } + } + if (!tValidValue) { + tTemp2.copyFrom(tDivider); + tTemp2.mul(tFastCoef - 1); + int tTemp2IntLength = getIntLength(tTemp2); + if ((tTemp2IntLength + tDividerIntLength) > tRemainderIntLength) { + tDigitCount = tDigitCount - pDigits; + } + tTemp2.copyFrom(tDivider); + tTemp2.mul(tFastCoef - 1); + for (int i = 1; i <= tDigitCount; i++) { + tTemp2.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.addAt(1, 0); + } + if (tTemp2.greaterThan(tRemainder)) { + tFastCoef = tFastCoef - 1; + tValidValue = 1; + } + } + if (!tValidValue) { + tTemp.copyFrom(tDivider); + tTemp.mul(tFastCoef + 1); + for (int i = 1; i <= tDigitCount; i++) { + if (tDividerDigits == pDigits) { + tTempInt = tRemainder.getIntValue(tRemainderDigits - tDividerDigits + 1); + } else { + tTempString = ""; + for (int j = 0; j < tDividerDigits; j++) { + int tIndex = tRemainderDigits - tDividerDigits + j + 1; + if (tIndex <= tRemainderDigits) { + tTempString += Integer.toString(tRemainder.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.get(tIndex)); + } + } + tTempInt = Integer.parseInt(tTempString); + } + tFastCoef = tTempInt / tDividerInt; + tTemp.copyFrom(tDivider); + tTemp.mul(tFastCoef); + tAddCountNew = tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() - tRemainderDigits; + if (tAddCountNew > 0) { + int tTempCount = tAddCountNew - tAddCount; + for (int j = 1; j <= tTempCount; j++) { + tResultData.add(0); + } + tAddCount = tAddCountNew; + } + if (tAddCount > 0) { + for (int j = 1; j <= tAddCount; j++) { + tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.addAt(1, 0); + } + } + boolean tValidValue = false; + if (!tTemp.greaterThan(tRemainder)) { + if (tAddCountNew == tAddCount) { + tFastCoef--; + tValidValue = true; + } else { + tTemp2.copyFrom(tDivider); + tTemp2.mul(tFastCoef); + for (int j = 1; j <= tAddCount - 1; j++) { + tTemp2.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.addAt(1, 0); + } + if (tTemp2.greaterThan(tTemp)) { + tAddCount--; + } else { + tFastCoef--; + tValidValue = true; + } + } + } else { + tAddCount--; + } + if (!tValidValue) { + tTemp.copyFrom(tDivider); + tTemp.mul(tFastCoef); + for (int j = 1; j <= tAddCount; j++) { + tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.addAt(1, 0); + } + } + tResultData.add(tFastCoef); + tRemainder = tRemainder.dif(tTemp); + if (tRemainder.isZero()) { + for (int j = 1; j <= tAddCount; j++) { + tResultData.add(0); + } + break; + } + if (!tRemainder.lessThan(tDivider)) { + tFastCoef = tFastCoef + 1; + tTemp.copyFrom(tDivider); + tTemp.add(tDivider); + while (tTemp.lessThan(tRemainder)) { + tFastCoef = tFastCoef + 1; + tTemp.add(tDivider); + } + tTemp.sub(tDivider); + int tAddCountNew = tRemainder.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size() - tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.size(); + if (tAddCountNew > tAddCount) { + tAddCount = tAddCountNew; + } + tValidValue = 0; + if (!tTemp.greaterThan(tRemainder)) { + if (tAddCountNew == tAddCount) { + tFastCoef = tFastCoef - 1; + tValidValue = 1; + } else { + tTemp2.copyFrom(tDivider); + tTemp2.mul(tFastCoef); + for (int i = 1; i <= tAddCount - 1; i++) { + tTemp2.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.addAt(0, 0); + } + if (tTemp2.greaterThan(tTemp)) { + tAddCount = tAddCount - 1; + } else { + tFastCoef = tFastCoef - 1; + tValidValue = 1; + } + } + } else { + tAddCount = tAddCount - 1; + } + if (tValidValue == 0) { + tTemp.copyFrom(tDivider); + tTemp.mul(tFastCoef); + for (int i = 1; i <= tAddCount; i++) { + tTemp.pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.addAt(0, 0); + } + } + } +*/ +} \ No newline at end of file diff --git a/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/SecurityCode.java b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/SecurityCode.java index 69a2df0..0c4a2c0 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/SecurityCode.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/game/encryption/SecurityCode.java @@ -1,61 +1,29 @@ package org.alexdev.havana.game.encryption; -import java.util.ArrayList; -import java.util.List; - public class SecurityCode { - // 366592457230440492243310979215656075163725460776 - - public static String assign(Object obj) { - List pData_NxIhNARqldyJyY2PfT03dK8t9OLUR = new ArrayList<>(); - - if (obj instanceof String) { - String hex = (String) obj; - String tData = hex; - - if (hex.toCharArray()[0] == '-') { - tData = hex.substring(1); - } - - int i = tData.length(); - int pDigits = "10000".length() - 1; - - while (i > 0) { - String tCoef = tData.substring(Math.max(0, i - (pDigits)), i); - i = i - tCoef.length(); - pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.add("" + Integer.parseInt(tCoef)); - } + public static int encode(int tPlain) { + int tSeed = 5678; + int[] tSBox = new int[]{7530, 6652, 4115, 1750, 3354, 3647, 5188, 2844, 818, 2026, 7133, 2592, 3578}; + int tIterations = 54; + int tCipher = tPlain; + for (int i = 1; i <= tIterations; i++) { + tSeed = ((69069 * tSeed) + (139 * i) + 92541) % 10000; + tSeed = tSeed + (int) Math.pow(i, 3); + tSeed = ((tSBox[(i % tSBox.length)] * tSeed) + 2541) % 10000; + tCipher = (tSeed ^ tCipher); + tCipher = (1379 + tSBox[(i % tSBox.length)]) ^ tCipher; + tCipher = (((14 * tSBox[(i % tSBox.length)]) + 13) % 10000) ^ tCipher; + tCipher = tCipher * 2; + int tHighBit = tCipher & 32768; + tCipher = tCipher & 32767; + tCipher = tCipher | (tHighBit != 0 ? 1 : 0); } - - if (obj instanceof int[]) { - int[] parameters = (int[]) obj; - - int tLimit = parameters.length; - - for (int tdata : parameters) { - pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.add(""); - } - - /*int i = 0; - for (int tdata : parameters) { - pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.add("" + decode(tdata)); - i++; - } - - Collections.reverse(pData_NxIhNARqldyJyY2PfT03dK8t9OLUR);*/ - int i = 0; - for (int tdata : parameters) { - pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.set(tLimit - i - 1, "" + decode(tdata)); - i++; - } - - //Collections.reverse(pData_NxIhNARqldyJyY2PfT03dK8t9OLUR); - } - - return String.join("", pData_NxIhNARqldyJyY2PfT03dK8t9OLUR);//Arrays.toString(pData_NxIhNARqldyJyY2PfT03dK8t9OLUR.toArray()); + tCipher = 7639 ^ tCipher; + return tCipher; } - private static int decode(int tInput) { + + public static int decode(int tInput) { int tSeed = 5678; int[] tSBox = new int[]{7530, 6652, 4115, 1750, 3354, 3647, 5188, 2844, 818, 2026, 7133, 2592, 3578}; diff --git a/Havana-Server/src/main/java/org/alexdev/havana/game/player/Player.java b/Havana-Server/src/main/java/org/alexdev/havana/game/player/Player.java index c344b7c..3aaf792 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/game/player/Player.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/game/player/Player.java @@ -6,8 +6,8 @@ import org.alexdev.havana.game.achievements.user.UserAchievementManager; import org.alexdev.havana.game.badges.BadgeManager; import org.alexdev.havana.game.club.ClubSubscription; import org.alexdev.havana.game.effects.Effect; +import org.alexdev.havana.game.encryption.Cryptography; import org.alexdev.havana.game.encryption.DiffieHellman; -import org.alexdev.havana.game.encryption.RC4; import org.alexdev.havana.game.entity.Entity; import org.alexdev.havana.game.entity.EntityType; import org.alexdev.havana.game.fuserights.Fuseright; @@ -33,12 +33,14 @@ import org.alexdev.havana.messages.outgoing.openinghours.INFO_HOTEL_CLOSING; import org.alexdev.havana.messages.outgoing.user.settings.HELP_ITEMS; import org.alexdev.havana.messages.types.MessageComposer; import org.alexdev.havana.server.netty.NettyPlayerNetwork; +import org.alexdev.havana.server.netty.ServerHandlerType; import org.alexdev.havana.util.DateUtil; import org.alexdev.havana.util.StringUtil; import org.alexdev.havana.util.config.GameConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.math.BigInteger; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; @@ -60,18 +62,16 @@ public class Player extends Entity { private CopyOnWriteArrayList effects; private Set ignoredList; - private DiffieHellman diffieHellman; - private RC4 rc4; private boolean loggedIn; private boolean disconnected; private boolean pingOK; - private boolean hasGenerateKey; private long timeConnected; private boolean processLoginSteps; private List joinedGroups; private String lastGift; + private boolean hasEncryption; public Player(NettyPlayerNetwork nettyPlayerNetwork) { this.network = nettyPlayerNetwork; @@ -494,6 +494,20 @@ public class Player extends Entity { return diffieHellman; } + /* + * Sets the rc4. + * + * @param sharedKey the new rc4 + */ + public void setDecoder(BigInteger sharedKey) { + this.hasEncryption = true; + this.network.registerHandler(ServerHandlerType.RC4, sharedKey); + } + + public boolean hasEncryption() { + return hasEncryption; + } + /** * Get the list of user activated effects. * @@ -503,24 +517,6 @@ public class Player extends Entity { return effects; } - /** - * Get if the user has used the generate key - * - * @return true, if successful - */ - public boolean hasGenerateKey() { - return hasGenerateKey; - } - - /** - * Set whether the user has generated the key - * - * @param hasGenerateKey the flag - */ - public void setHasGenerateKey(boolean hasGenerateKey) { - this.hasGenerateKey = hasGenerateKey; - } - /** * Get the user ignored list. * diff --git a/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/GENERATEKEY.java b/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/GENERATEKEY.java index bf48323..f93b888 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/GENERATEKEY.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/GENERATEKEY.java @@ -1,11 +1,13 @@ package org.alexdev.havana.messages.incoming.handshake; -import org.alexdev.havana.game.encryption.DiffieHellman; +import org.alexdev.havana.game.encryption.HugeInt15; import org.alexdev.havana.game.player.Player; import org.alexdev.havana.messages.outgoing.handshake.SECRET_KEY; import org.alexdev.havana.messages.types.MessageEvent; import org.alexdev.havana.server.netty.streams.NettyRequest; +import java.util.Arrays; + public class GENERATEKEY implements MessageEvent { @Override @@ -15,10 +17,15 @@ public class GENERATEKEY implements MessageEvent { } String publicKey = reader.readString(); - player.getDiffieHellman().generateSharedKey(publicKey); - player.setHasGenerateKey(true); - player.send(new SECRET_KEY(DiffieHellman.generateRandomNumString(24)));//player.getDetails())); + player.send(new SECRET_KEY(player.getDiffieHellman().getPublicKey().toString())); + player.setDecoder(player.getDiffieHellman().getSharedKey()); + + //System.out.println("publicKey: " + publicKey); + //System.out.println("adobeClientSharedKey: " + adobeClientSharedKey.getString()); + + //System.out.println("sharedKey: " + player.getDiffieHellman().getSharedKey()); + //System.out.println("sharedKey byte array: " + Arrays.toString(HugeInt15.getByteArray(player.getDiffieHellman().getSharedKey()))); } } diff --git a/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/INIT_CRYPTO.java b/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/INIT_CRYPTO.java index 3044912..fc5c229 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/INIT_CRYPTO.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/messages/incoming/handshake/INIT_CRYPTO.java @@ -1,9 +1,9 @@ package org.alexdev.havana.messages.incoming.handshake; import org.alexdev.havana.game.GameScheduler; +import org.alexdev.havana.game.encryption.DiffieHellman; import org.alexdev.havana.game.player.Player; import org.alexdev.havana.messages.outgoing.handshake.CRYPTO_PARAMETERS; -import org.alexdev.havana.messages.outgoing.handshake.SESSION_PARAMETERS; import org.alexdev.havana.messages.types.MessageEvent; import org.alexdev.havana.server.netty.streams.NettyRequest; @@ -17,33 +17,10 @@ public class INIT_CRYPTO implements MessageEvent { return; } - //String prime = Util.getRSA().sign(dh.getPrime().toString()); - //String generator = Util.getRSA().sign(dh.getGenerator().toString()); + String pToken = DiffieHellman.generateRandomNumString(24); - //player.sendObject("DAQBHIIIKHJIPAIQAdd-MM-yyyy\u0002SAHPB/client\u0002QBHIJWVVVSNKQCFUBJASMSLKUUOJCOLJQPNSBIRSVQBRXZQOTGPMNJIHLVJCRRULBLUO" + (char)1); - - player.send(new CRYPTO_PARAMETERS()); - - // Try again - this.retrySend(player); + player.send(new CRYPTO_PARAMETERS(pToken)); + player.getNetwork().setToken(pToken); } - /** - * Retry sending the crypto parameters if after a second we received no response from the client. - * - * @param player the player to send the parameters to - */ - private void retrySend(Player player) { - GameScheduler.getInstance().getService().schedule(() -> { - if (player.isDisconnected()) { - return; - } - - if (player.hasGenerateKey()) { - return; - } - - player.send(new CRYPTO_PARAMETERS()); - }, 1, TimeUnit.SECONDS); - } } diff --git a/Havana-Server/src/main/java/org/alexdev/havana/messages/outgoing/handshake/CRYPTO_PARAMETERS.java b/Havana-Server/src/main/java/org/alexdev/havana/messages/outgoing/handshake/CRYPTO_PARAMETERS.java index ce8273f..65dd5ab 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/messages/outgoing/handshake/CRYPTO_PARAMETERS.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/messages/outgoing/handshake/CRYPTO_PARAMETERS.java @@ -5,11 +5,16 @@ import org.alexdev.havana.messages.types.MessageComposer; import org.alexdev.havana.server.netty.streams.NettyResponse; public class CRYPTO_PARAMETERS extends MessageComposer { + private String token; + + public CRYPTO_PARAMETERS(String token) { + this.token = token; + } @Override public void compose(NettyResponse response) { - response.writeString(DiffieHellman.generateRandomNumString(32)); - response.writeInt(0); + response.writeString(this.token); + response.writeBool(true); } @Override diff --git a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyChannelInitializer.java b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyChannelInitializer.java index 3a923b0..02999f2 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyChannelInitializer.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyChannelInitializer.java @@ -29,8 +29,8 @@ public class NettyChannelInitializer extends ChannelInitializer { pipeline.addLast("trafficShapingHandler", new ChannelTrafficShapingHandler(bandwidthLimit, bandwidthLimit)); } - pipeline.addLast("gameEncoder", new NetworkEncoder()); - pipeline.addLast("gameDecoder", new NetworkDecoder()); + pipeline.addLast("networkEncoder", new NetworkEncoder()); + pipeline.addLast("networkDecoder", new NetworkDecoder()); pipeline.addLast("handler", new ConnectionHandler(this.nettyServer)); pipeline.addLast("idleStateHandler", new IdleStateHandler(60, 0, 0)); pipeline.addLast("idleHandler", new IdleConnectionHandler()); diff --git a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyPlayerNetwork.java b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyPlayerNetwork.java index 9e2b937..7c9151e 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyPlayerNetwork.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/NettyPlayerNetwork.java @@ -1,25 +1,109 @@ package org.alexdev.havana.server.netty; import io.netty.channel.Channel; -import org.alexdev.havana.Havana; -import org.alexdev.havana.dao.mysql.PlayerDao; -import org.alexdev.havana.game.player.PlayerManager; -import org.alexdev.havana.util.StringUtil; -import org.alexdev.havana.util.config.ServerConfiguration; +import org.alexdev.havana.server.netty.codec.EncryptionDecoder; +import org.alexdev.havana.server.netty.codec.EncryptionEncoder; import org.apache.commons.validator.routines.InetAddressValidator; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + public class NettyPlayerNetwork { private Channel channel; private int connectionId; private boolean saveMachineId; private String clientMachineId; + private String pToken; + private int pTx; + private int pRx; public NettyPlayerNetwork(Channel channel, int connectionId) { this.channel = channel; this.connectionId = connectionId; } + public static String removePadding(String tBody, int i) { + if (i >= tBody.length()) + return tBody; + + return tBody.substring(i); + } + + public static String addPad(String tMsg, int amount) { + var secureRandom = new SecureRandom(); + + for (int i = 0; i < amount; i++) { + tMsg = Character.toString(secureRandom.nextInt(255) + 1) + tMsg; + } + + return tMsg; + } + + public void setToken(String tToken) { + this.pToken = tToken; + String tSeedHex = pToken.substring(pToken.length() - 4); + Map tVals = new HashMap<>(); + tVals.put("0", 0); + tVals.put("1", 1); + tVals.put("2", 2); + tVals.put("3", 3); + tVals.put("4", 4); + tVals.put("5", 5); + tVals.put("6", 6); + tVals.put("7", 7); + tVals.put("8", 8); + tVals.put("9", 9); + tVals.put("a", 10); + tVals.put("b", 11); + tVals.put("c", 12); + tVals.put("d", 13); + tVals.put("e", 14); + tVals.put("f", 15); + tVals.put("A", 10); + tVals.put("B", 11); + tVals.put("C", 12); + tVals.put("D", 13); + tVals.put("E", 14); + tVals.put("F", 15); + + this.pTx = 0; + + for (int i = 0; i <= 3; i++) { + this.pTx += (int)(Math.pow(16, i) * tVals.get(Character.toString(tSeedHex.charAt(3 - i)))); + } + + this.pRx = 0; + + tSeedHex = pToken.substring(0, 4); + + for (int i = 0; i <= 3; i++) { + this.pRx += (int)(Math.pow(16, i) * tVals.get(Character.toString(tSeedHex.charAt(3 - i)))); + } + } + + public static int iterateRandom(int tSeed) { + return ((19979 * tSeed) + 5) % 65536; + } + + public int getTx() { + return pTx; + } + + public int getRx() { + return pRx; + } + + public void setTx(int pTx) { + this.pTx = pTx; + } + + public void setRx(int pRx) { + this.pRx = pRx; + } + public Channel getChannel() { return this.channel; } @@ -73,4 +157,11 @@ public class NettyPlayerNetwork { this.saveMachineId = saveMachineId; } + public void registerHandler(ServerHandlerType type, Object object) { + if (type == ServerHandlerType.RC4) { + this.channel.pipeline().addBefore("networkDecoder", "encryptionDecoder", new EncryptionDecoder((BigInteger) object)); + this.channel.pipeline().addBefore("networkEncoder", "encryptionEncoder", new EncryptionEncoder((BigInteger) object)); + // this.channel.pipeline().remove("gameDecoder"); + } + } } diff --git a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionDecoder.java b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionDecoder.java index 93671ea..430ab61 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionDecoder.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionDecoder.java @@ -4,26 +4,72 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; -import org.alexdev.havana.game.encryption.RC4; +import org.alexdev.havana.game.encryption.Cryptography; +import org.alexdev.havana.game.encryption.HugeInt15; +import org.alexdev.havana.game.player.Player; +import org.alexdev.havana.server.netty.NettyPlayerNetwork; +import org.alexdev.havana.server.netty.streams.NettyRequest; +import org.alexdev.havana.util.StringUtil; +import org.alexdev.havana.util.encoding.Base64Encoding; +import java.math.BigInteger; import java.util.List; public class EncryptionDecoder extends ByteToMessageDecoder { + private Cryptography pHeaderDecoder; + private Cryptography pDecoder; - private RC4 rc4; - - public EncryptionDecoder(RC4 rc4) { - this.rc4 = rc4; + public EncryptionDecoder(BigInteger sharedKey) { + this.pHeaderDecoder = new Cryptography(HugeInt15.getByteArray(sharedKey)); + this.pDecoder = new Cryptography(HugeInt15.getByteArray(sharedKey)); } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { - ByteBuf result = Unpooled.buffer(); + Player player = ctx.channel().attr(Player.PLAYER_KEY).get(); - while (buffer.readableBytes() > 0) { - result.writeByte((byte) (buffer.readByte() ^ this.rc4.next())); + String tHeader; + String tBody; + + int pMsgSize; + + buffer.markReaderIndex(); + + while (buffer.readableBytes() > 6) { + byte[] tHeaderMsg = new byte[6]; + buffer.readBytes(tHeaderMsg); + + tHeader = new String(tHeaderMsg, StringUtil.getCharset()); + tHeader = this.pHeaderDecoder.kg4R6Jo5xjlqtFGs1klMrK4ZTzb3R(tHeader); + + int tByte1 = ((int) tHeader.charAt(3)) & 63; + int tByte2 = ((int) tHeader.charAt(2)) & 63; + int tByte3 = ((int) tHeader.charAt(1)) & 63; + pMsgSize = (tByte2 * 64) | tByte1; + pMsgSize = (tByte3 * 64 * 64) | pMsgSize; + + if (buffer.readableBytes() < pMsgSize) { + buffer.resetReaderIndex(); + return; + } + + player.getNetwork().setTx( + NettyPlayerNetwork.iterateRandom(player.getNetwork().getTx()) + ); + + byte[] tBodyMsg = new byte[pMsgSize]; + buffer.readBytes(tBodyMsg); + + tBody = new String(tBodyMsg, StringUtil.getCharset()); + tBody = this.pDecoder.kg4R6Jo5xjlqtFGs1klMrK4ZTzb3R(tBody); + tBody = NettyPlayerNetwork.removePadding(tBody, player.getNetwork().getTx() % 5); + + ByteBuf result = Unpooled.buffer(); + + result.writeBytes(Base64Encoding.encode(tBody.length(), 3)); + result.writeBytes(tBody.getBytes(StringUtil.getCharset())); + + out.add(result); } - - out.add(result); } } \ No newline at end of file diff --git a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionEncoder.java b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionEncoder.java new file mode 100644 index 0000000..b75e779 --- /dev/null +++ b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/EncryptionEncoder.java @@ -0,0 +1,61 @@ +package org.alexdev.havana.server.netty.codec; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import org.alexdev.havana.game.encryption.Cryptography; +import org.alexdev.havana.game.encryption.HugeInt15; +import org.alexdev.havana.game.player.Player; +import org.alexdev.havana.server.netty.NettyPlayerNetwork; +import org.alexdev.havana.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class EncryptionEncoder extends MessageToMessageEncoder { + private static final Logger log = LoggerFactory.getLogger(EncryptionEncoder.class); + + private Cryptography pHeaderEncoder; + private Cryptography pEncoder; + + public EncryptionEncoder(BigInteger sharedKey) { + this.pHeaderEncoder = new Cryptography(HugeInt15.getByteArray(sharedKey)); + this.pEncoder = new Cryptography(HugeInt15.getByteArray(sharedKey)); + } + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { + Player player = ctx.channel().attr(Player.PLAYER_KEY).get(); + + player.getNetwork().setRx(NettyPlayerNetwork.iterateRandom( + player.getNetwork().getRx() + )); + + String tOriginalMsg = buffer.toString(StringUtil.getCharset()); + + String tHeader; + String tMsg; + + tMsg = NettyPlayerNetwork.addPad(tOriginalMsg, player.getNetwork().getRx() % 5); + tMsg = this.pEncoder.AkwGx8bHG2kc1xGG4xbdHPCV0fqvK(tMsg); + + var tLength = tMsg.length(); + var tL1 = Character.toString((tLength & 63) | 64); + var tL2 = Character.toString(((tLength / 64) & 63) | 64); + var tL3 = Character.toString(((tLength / 4096) & 63) | 64); + + tHeader = (char) (ThreadLocalRandom.current().nextInt(127) + 1) + (tL3 + tL2 + tL1); + tHeader = this.pHeaderEncoder.AkwGx8bHG2kc1xGG4xbdHPCV0fqvK(tHeader); + + var tEncryptedMsg = Unpooled.buffer(); + + tEncryptedMsg.writeBytes(tHeader.getBytes(StringUtil.getCharset())); + tEncryptedMsg.writeBytes(tMsg.getBytes(StringUtil.getCharset())); + + out.add(tEncryptedMsg); + } +} \ No newline at end of file diff --git a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkDecoder.java b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkDecoder.java index 86dfeee..7508818 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkDecoder.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkDecoder.java @@ -7,18 +7,20 @@ import org.alexdev.havana.game.player.Player; import org.alexdev.havana.server.netty.streams.NettyRequest; import org.alexdev.havana.util.encoding.Base64Encoding; +import java.nio.charset.Charset; import java.util.List; public class NetworkDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) { - if (buffer.readableBytes() < 5) { + if (buffer.readableBytes() < 4) { // If the incoming data is less than 5 bytes, it's junk. return; } buffer.markReaderIndex(); + int length = Base64Encoding.decode(new byte[]{buffer.readByte(), buffer.readByte(), buffer.readByte()}); if (buffer.readableBytes() < length) { diff --git a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkEncoder.java b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkEncoder.java index a6b4f2f..cb8042a 100644 --- a/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkEncoder.java +++ b/Havana-Server/src/main/java/org/alexdev/havana/server/netty/codec/NetworkEncoder.java @@ -1,7 +1,9 @@ package org.alexdev.havana.server.netty.codec; import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToMessageEncoder; import org.alexdev.havana.game.player.Player; import org.alexdev.havana.log.Log; @@ -15,51 +17,38 @@ import org.alexdev.havana.util.config.ServerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.Buffer; import java.util.List; -public class NetworkEncoder extends MessageToMessageEncoder { +public class NetworkEncoder extends MessageToMessageEncoder { final private static Logger log = LoggerFactory.getLogger(NetworkEncoder.class); @Override - protected void encode(ChannelHandlerContext ctx, Object obj, List out) throws Exception { + protected void encode(ChannelHandlerContext ctx, MessageComposer msg, List out) throws Exception { Player player = ctx.channel().attr(Player.PLAYER_KEY).get(); - ByteBuf buffer = ctx.alloc().buffer(); + ByteBuf buffer = Unpooled.buffer(); - if (obj instanceof MessageComposer) { - MessageComposer msg = (MessageComposer) obj; - - if (obj instanceof PlayerMessageComposer) { - PlayerMessageComposer playerMessageComposer = (PlayerMessageComposer) obj; - playerMessageComposer.setPlayer(player); - } - - NettyResponse response = new NettyResponse(msg.getHeader(), buffer); - - try { - msg.compose(response); - } catch (Exception ex) { - String name = ""; - - if (player != null && player.isLoggedIn()) { - name = player.getDetails().getName(); - } - - Log.getErrorLogger().error("Error occurred when composing (" + response.getHeader() + ") for user (" + name + "):", ex); - return; - } - - if (!response.isFinalised()) { - buffer.writeByte(1); - response.setFinalised(true); - } - - if (ServerConfiguration.getBoolean("log.sent.packets")) { - log.info("SENT: {} / {}", msg.getHeader(), response.getBodyString()); - } + if (msg instanceof PlayerMessageComposer) { + PlayerMessageComposer playerMessageComposer = (PlayerMessageComposer) msg; + playerMessageComposer.setPlayer(player); } - if (obj instanceof String) { - buffer.writeBytes(((String) obj).getBytes()); + NettyResponse response = new NettyResponse(msg.getHeader(), buffer); + + try { + msg.compose(response); + } catch (Exception ex) { + Log.getErrorLogger().error("Error occurred when composing (" + response.getHeader() + "):", ex); + return; + } + + if (!response.isFinalised()) { + buffer.writeByte(1); + response.setFinalised(true); + } + + if (ServerConfiguration.getBoolean("log.sent.packets")) { + log.info("SENT 1: {} / {}", msg.getHeader(), response.getBodyString()); } out.add(buffer);