allow keeping the same trypt instance for a while, massively improving security of chatcrypt

This commit is contained in:
Daniella / Tove 2024-05-25 01:04:48 +02:00
parent d3a7346ece
commit c8ea8ae662
3 changed files with 68 additions and 14 deletions

View file

@ -2,8 +2,10 @@ package com.baseband.client.module.chat;
import com.baseband.client.configuration.annotation.Config; import com.baseband.client.configuration.annotation.Config;
import com.baseband.client.configuration.annotation.Gate; import com.baseband.client.configuration.annotation.Gate;
import com.baseband.client.configuration.annotation.MultiGate;
import com.baseband.client.configuration.annotation.Range; import com.baseband.client.configuration.annotation.Range;
import com.baseband.client.event.events.PacketEvent; import com.baseband.client.event.events.PacketEvent;
import com.baseband.client.gui.lib.component.Button;
import com.baseband.client.mixins.ICPacketChat; import com.baseband.client.mixins.ICPacketChat;
import com.baseband.client.module.Feature; import com.baseband.client.module.Feature;
import com.baseband.client.module.category.Chat; import com.baseband.client.module.category.Chat;
@ -49,10 +51,12 @@ public class ChatCrypt extends Feature {
@Config("Use SBE algorithm (preferred)") @Config("Use SBE algorithm (preferred)")
@Marker(1) @Marker(1)
public boolean useSBE = true; public boolean useSBE = true;
@Marker(2)
public boolean useTrypt = false;
@Config("(But Trypt looks more random)") @Config("(But Trypt looks more random)")
@Marker(2) @Marker(-1)
@Gate(2) @Gate(-1)
public boolean _trypt_info = true; public boolean _trypt_info = true;
@Config("Seed") @Config("Seed")
@ -64,13 +68,29 @@ public class ChatCrypt extends Feature {
@Gate(1) @Gate(1)
public int boxSize = 256; public int boxSize = 256;
@Config("Keep Trypt instance")
@Gate(2)
@Marker(3)
public boolean keepTrypt = false;
@Config("Warning! More secure, worse QOL")
@MultiGate(and = {3, -2})
@Marker(-2)
public boolean _keep_info = true;
@Config("Reset Trypt")
@Gate(3)
public Button.ClickEvent resetTrypt = btn -> {
trypt = null;
};
private Trypt trypt;
private final Pattern CHAT_PATTERN = Pattern.compile("(<.*?> )|(.*?: )"); private final Pattern CHAT_PATTERN = Pattern.compile("(<.*?> )|(.*?: )");
@Config("Password") @Config("Password")
public String password = "CLICK HERE"; public String password = "CLICK HERE";
public void onEnable() { public void onEnable() {
if(password.isEmpty() || password.equalsIgnoreCase("CLICK HERE")) { if(password.isEmpty() || password.equalsIgnoreCase("CLICK HERE")) {
toggle(); toggle();
@ -78,6 +98,12 @@ public class ChatCrypt extends Feature {
} }
} }
@Override
public void onEveryTick() {
useTrypt = !useSBE;
}
String sentSomething = null;
public void onPacketRead(PacketEvent.Read e) { public void onPacketRead(PacketEvent.Read e) {
if (e.getPacket() instanceof SPacketChat) { if (e.getPacket() instanceof SPacketChat) {
@ -97,8 +123,23 @@ public class ChatCrypt extends Feature {
message = message.substring(0, message.length() - getTerminator().length()); message = message.substring(0, message.length() - getTerminator().length());
} }
boolean isOurs = message.equals(sentSomething);
sentSomething = null;
GlobalUtil.LOGGER.info("decrypt: {}", message); GlobalUtil.LOGGER.info("decrypt: {}", message);
message = decrypt(message); byte[] original = recoverBytes(message);
message = decrypt(original);
if(!useSBE && keepTrypt && !isOurs) {
// we must re-encrypt anything we get, unless it is from ourselves, to make sure our key stays up-to-date
if(Arrays.equals(trypt.encryptChunk(message.getBytes(StandardCharsets.UTF_8), original[0]), original)) {
GlobalUtil.LOGGER.debug("Successfully kept Trypt key up-to-date.");
}
else {
HUD.notifyAndPrint("§c§lChat>§c Unable to keep Trypt key up-to-date. You must reset it.");
}
}
try { try {
FieldFinder.findUnmarked(SPacketChat.class, ITextComponent.class, 0).set(e.getPacket(), new TextComponentString("§dChatCrypt> §r" + username + ": " + message)); FieldFinder.findUnmarked(SPacketChat.class, ITextComponent.class, 0).set(e.getPacket(), new TextComponentString("§dChatCrypt> §r" + username + ": " + message));
} catch (IllegalAccessException ex) { } catch (IllegalAccessException ex) {
@ -127,10 +168,13 @@ public class ChatCrypt extends Feature {
if(s.startsWith("/")) if(s.startsWith("/"))
return; return;
s = encrypt(s) + getTerminator(); s = encrypt(s);
sentSomething = s;
s += getTerminator();
if (s.length() > 256) { if (s.length() > 256) {
ChatUtil.print("Encrypted message length was too long, couldn't send!"); ChatUtil.print("Encrypted message length was too long, couldn't send!");
e.setCancelled(true); e.setCancelled(true);
sentSomething = null;
} }
((ICPacketChat)e.getPacket()).setMessage(s); ((ICPacketChat)e.getPacket()).setMessage(s);
} }
@ -142,20 +186,22 @@ public class ChatCrypt extends Feature {
return armorBytes(sbe.transform(value.getBytes(StandardCharsets.UTF_8))); return armorBytes(sbe.transform(value.getBytes(StandardCharsets.UTF_8)));
} }
else { else {
Trypt trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8)); if(!keepTrypt || trypt == null)
trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8));
return armorBytes(trypt.encryptChunk(value.getBytes(StandardCharsets.UTF_8))); return armorBytes(trypt.encryptChunk(value.getBytes(StandardCharsets.UTF_8)));
} }
} }
public String decrypt(String encrypted) { public String decrypt(byte[] encrypted) {
if(useSBE) { if(useSBE) {
SBE sbe = new SBE(Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8), boxSize, seed); SBE sbe = new SBE(Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8), boxSize, seed);
return new String(sbe.transform(recoverBytes(encrypted)), StandardCharsets.US_ASCII); return new String(sbe.transform(encrypted), StandardCharsets.US_ASCII);
} }
else { else {
Trypt trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8)); if(!keepTrypt || trypt == null)
return new String(trypt.decryptChunk(recoverBytes(encrypted)), StandardCharsets.UTF_8); trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8));
return new String(trypt.decryptChunk(encrypted), StandardCharsets.UTF_8);
} }
} }

View file

@ -7,6 +7,7 @@ import com.baseband.client.module.Category;
import com.baseband.client.module.Feature; import com.baseband.client.module.Feature;
import com.baseband.client.module.category.Render; import com.baseband.client.module.category.Render;
import com.baseband.client.module.client.Client; import com.baseband.client.module.client.Client;
import com.baseband.client.util.ingame.ChatUtil;
import com.baseband.client.util.misc.Marker; import com.baseband.client.util.misc.Marker;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.Gui;
@ -50,6 +51,11 @@ public class HUD extends Feature {
} }
} }
public static void notifyAndPrint(String text) {
notifs.add(new Notification(text));
ChatUtil.print(text);
}
public static void notify(String text) { public static void notify(String text) {
notifs.add(new Notification(text)); notifs.add(new Notification(text));
} }

View file

@ -18,15 +18,18 @@ public class Trypt {
cRandD = new Random(seed); cRandD = new Random(seed);
this.key = key; this.key = key;
} }
public byte[] encryptChunk(byte[] input) { public byte[] encryptChunk(byte[] input) {
return encryptChunk(input, (byte) sRand.nextInt());
}
public byte[] encryptChunk(byte[] input, byte salt) {
ArrayList<Byte> data = new ArrayList<>(input.length); ArrayList<Byte> data = new ArrayList<>(input.length);
for (byte b : input) { for (byte b : input) {
data.add(b); data.add(b);
} }
ArrayList<Integer> forbidden = new ArrayList<>(input.length); ArrayList<Integer> forbidden = new ArrayList<>(input.length);
byte[] output = new byte[input.length + 1]; byte[] output = new byte[input.length + 1];
byte salt = (byte) sRand.nextInt();
output[0] = salt; output[0] = salt;
for(int i = 0; i < input.length;) { for(int i = 0; i < input.length;) {
@ -36,7 +39,6 @@ public class Trypt {
forbidden.add(newLocation); forbidden.add(newLocation);
byte old = data.remove(0); byte old = data.remove(0);
output[newLocation + 1] = (byte) (old ^ salt ^ key[(i++ ^ cRandE.nextInt(0x7fffffff)) % key.length]); output[newLocation + 1] = (byte) (old ^ salt ^ key[(i++ ^ cRandE.nextInt(0x7fffffff)) % key.length]);
//input[(newLocation + 1) % input.length] -= (byte) (old ^ cRandE.nextInt(0x7fffffff));
salt = (byte) (salt ^ (newLocation << cRandE.nextInt(8))); salt = (byte) (salt ^ (newLocation << cRandE.nextInt(8)));
} }