Test
This commit is contained in:
parent
22a7d78152
commit
250b36f9ee
11
build.gradle
11
build.gradle
@ -12,9 +12,18 @@ repositories {
|
|||||||
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
|
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
|
||||||
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
|
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
|
||||||
// for more information about repositories.
|
// for more information about repositories.
|
||||||
|
maven {
|
||||||
|
name 'Xander Maven'
|
||||||
|
url 'https://maven.isxander.dev/releases'
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
name 'TerraformersMC'
|
||||||
|
url 'https://maven.terraformersmc.com/releases'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1'
|
||||||
// To change the versions see the gradle.properties file
|
// To change the versions see the gradle.properties file
|
||||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
||||||
@ -22,6 +31,8 @@ dependencies {
|
|||||||
|
|
||||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||||
|
modImplementation "dev.isxander.yacl:yet-another-config-lib-fabric:${project.yacl_version}"
|
||||||
|
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
|
@ -15,3 +15,5 @@ org.gradle.jvmargs=-Xmx1G
|
|||||||
# Dependencies
|
# Dependencies
|
||||||
# check this on https://modmuss50.me/fabric.html
|
# check this on https://modmuss50.me/fabric.html
|
||||||
fabric_version=0.96.11+1.20.4
|
fabric_version=0.96.11+1.20.4
|
||||||
|
yacl_version=3.3.2+1.20.4
|
||||||
|
modmenu_version=9.1.0-beta.1
|
||||||
|
24
src/main/java/systems/brn/chatencryptor/ChatCoder.java
Normal file
24
src/main/java/systems/brn/chatencryptor/ChatCoder.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package systems.brn.chatencryptor;
|
||||||
|
|
||||||
|
public class ChatCoder {
|
||||||
|
// Encode byte array to BMP characters
|
||||||
|
public static String encodeToBmp(byte[] byteArray) {
|
||||||
|
StringBuilder bmpText = new StringBuilder();
|
||||||
|
for (int i = 0; i < byteArray.length; i += 2) {
|
||||||
|
char bmpChar = (char) ((byteArray[i] & 0xFF) << 8 | (byteArray[i + 1] & 0xFF));
|
||||||
|
bmpText.append(bmpChar);
|
||||||
|
}
|
||||||
|
return bmpText.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode BMP characters to byte array
|
||||||
|
public static byte[] decodeFromBmp(String bmpString) {
|
||||||
|
byte[] byteArray = new byte[bmpString.length() * 2];
|
||||||
|
for (int i = 0; i < bmpString.length(); i++) {
|
||||||
|
char bmpChar = bmpString.charAt(i);
|
||||||
|
byteArray[i * 2] = (byte) ((bmpChar >> 8) & 0xFF);
|
||||||
|
byteArray[i * 2 + 1] = (byte) (bmpChar & 0xFF);
|
||||||
|
}
|
||||||
|
return byteArray;
|
||||||
|
}
|
||||||
|
}
|
134
src/main/java/systems/brn/chatencryptor/Config.java
Normal file
134
src/main/java/systems/brn/chatencryptor/Config.java
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package systems.brn.chatencryptor;
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import dev.isxander.yacl3.api.*;
|
||||||
|
import dev.isxander.yacl3.api.controller.StringControllerBuilder;
|
||||||
|
import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder;
|
||||||
|
import dev.isxander.yacl3.config.v2.api.ConfigClassHandler;
|
||||||
|
import dev.isxander.yacl3.config.v2.api.SerialEntry;
|
||||||
|
import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.SecretKeyFactory;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
public static ConfigClassHandler<Config> HANDLER = ConfigClassHandler.createBuilder(Config.class)
|
||||||
|
.id(new Identifier("securechat", "securechat_config"))
|
||||||
|
.serializer(config -> GsonConfigSerializerBuilder.create(config)
|
||||||
|
.setPath(FabricLoader.getInstance().getConfigDir().resolve("securechat.json"))
|
||||||
|
.appendGsonBuilder(GsonBuilder::setPrettyPrinting) // not needed, pretty print by default
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@SerialEntry
|
||||||
|
public boolean enabled = false;
|
||||||
|
@SerialEntry
|
||||||
|
public String SecretKey = "";
|
||||||
|
|
||||||
|
@SerialEntry
|
||||||
|
public String Iv = "";
|
||||||
|
public IvParameterSpec getRawIv() {
|
||||||
|
byte[] byteIv = Base64.getDecoder().decode(HANDLER.instance().Iv);
|
||||||
|
return new IvParameterSpec(byteIv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SecretKey getRawKey() {
|
||||||
|
byte[] binary_key = Base64.getDecoder().decode(HANDLER.instance().SecretKey);
|
||||||
|
// Convert byte array back to SecretKey
|
||||||
|
return new SecretKeySpec(binary_key, 0, binary_key.length, "AES");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean isEnabled() {
|
||||||
|
return HANDLER.instance().enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IvParameterSpec generateIv() {
|
||||||
|
byte[] iv = new byte[16];
|
||||||
|
new SecureRandom().nextBytes(iv);
|
||||||
|
return new IvParameterSpec(iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDefaultIv() {
|
||||||
|
IvParameterSpec generatedIv = generateIv();
|
||||||
|
byte [] bytesIv = generatedIv.getIV();
|
||||||
|
return Base64.getEncoder().encodeToString(bytesIv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey generateKey() {
|
||||||
|
KeyGenerator kg;
|
||||||
|
try {
|
||||||
|
kg = KeyGenerator.getInstance("AES");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
kg.init(256);
|
||||||
|
return kg.generateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDefaultKey() {
|
||||||
|
SecretKey generatedKey = generateKey();
|
||||||
|
byte[] encodedKey = generatedKey.getEncoded();
|
||||||
|
return Base64.getEncoder().encodeToString(encodedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setEnabled(Boolean newEnabled){
|
||||||
|
|
||||||
|
HANDLER.instance().enabled = newEnabled;
|
||||||
|
HANDLER.save();
|
||||||
|
}
|
||||||
|
private void setIv(String newIv){
|
||||||
|
|
||||||
|
HANDLER.instance().Iv = newIv;
|
||||||
|
HANDLER.save();
|
||||||
|
}
|
||||||
|
private void setSecretKey(String newSecretKey){
|
||||||
|
|
||||||
|
HANDLER.instance().SecretKey = newSecretKey;
|
||||||
|
HANDLER.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Screen makeConfig(Screen parentScreen) {
|
||||||
|
HANDLER.load();
|
||||||
|
return YetAnotherConfigLib.createBuilder()
|
||||||
|
.title(Text.literal("SecureChat Config."))
|
||||||
|
.category(ConfigCategory.createBuilder()
|
||||||
|
.name(Text.literal("General"))
|
||||||
|
.tooltip(Text.literal("Here you can set some generic settings."))
|
||||||
|
.group(OptionGroup.createBuilder()
|
||||||
|
.name(Text.literal("General"))
|
||||||
|
.description(OptionDescription.of(Text.literal("Here you can set some generic settings.")))
|
||||||
|
.option(Option.<Boolean>createBuilder()
|
||||||
|
.name(Text.literal("Enabled"))
|
||||||
|
.description(OptionDescription.of(Text.literal("This will enable the encryption.")))
|
||||||
|
.binding(false, () -> HANDLER.instance().enabled, this::setEnabled)
|
||||||
|
.controller(TickBoxControllerBuilder::create)
|
||||||
|
.build())
|
||||||
|
.option(Option.<String>createBuilder()
|
||||||
|
.name(Text.literal("Secret key"))
|
||||||
|
.description(OptionDescription.of(Text.literal("This be the key that will be used.")))
|
||||||
|
.binding(getDefaultKey(), () -> HANDLER.instance().Iv, this::setIv)
|
||||||
|
.controller(StringControllerBuilder::create)
|
||||||
|
.build())
|
||||||
|
.option(Option.<String>createBuilder()
|
||||||
|
.name(Text.literal("Initialization vector"))
|
||||||
|
.description(OptionDescription.of(Text.literal("This be the initialization vector that will be used.")))
|
||||||
|
.binding(getDefaultIv(), () -> HANDLER.instance().SecretKey, this::setSecretKey)
|
||||||
|
.controller(StringControllerBuilder::create)
|
||||||
|
.build())
|
||||||
|
.build())
|
||||||
|
.build())
|
||||||
|
.build().generateScreen(parentScreen);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package systems.brn.chatencryptor;
|
||||||
|
|
||||||
|
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||||
|
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||||
|
|
||||||
|
public class ModMenuIntegration implements ModMenuApi {
|
||||||
|
@Override
|
||||||
|
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||||
|
return parent -> {
|
||||||
|
Config config = new Config();
|
||||||
|
Config.HANDLER.load();
|
||||||
|
return config.makeConfig(parent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -17,46 +17,33 @@ import javax.crypto.Cipher;
|
|||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.*;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
|
|
||||||
public class SecureChat implements ClientModInitializer {
|
public class SecureChat implements ClientModInitializer {
|
||||||
|
|
||||||
private PublicKey publicKey;
|
|
||||||
private PrivateKey privateKey;
|
|
||||||
|
|
||||||
private void initKeys() {
|
|
||||||
KeyPairGenerator kpg;
|
|
||||||
try {
|
|
||||||
kpg = KeyPairGenerator.getInstance("RSA");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
kpg.initialize(512);
|
|
||||||
KeyPair kp = kpg.genKeyPair();
|
|
||||||
publicKey = kp.getPublic();
|
|
||||||
privateKey = kp.getPrivate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean decryptChatMessage(Text message, @Nullable SignedMessage signedMessage, @Nullable GameProfile sender, MessageType.Parameters params, Instant receptionTimestamp) {
|
private boolean decryptChatMessage(Text message, @Nullable SignedMessage signedMessage, @Nullable GameProfile sender, MessageType.Parameters params, Instant receptionTimestamp) {
|
||||||
TranslatableTextContent content = (TranslatableTextContent) message.getContent();
|
TranslatableTextContent content = (TranslatableTextContent) message.getContent();
|
||||||
String message_content = content.getArg(1).getString();
|
String message_content = content.getArg(1).getString();
|
||||||
String player_name = content.getArg(0).getString();
|
String player_name = content.getArg(0).getString();
|
||||||
if(message_content.startsWith("®") && message_content.endsWith("®")){
|
if(message_content.startsWith("®") && message_content.endsWith("®")){
|
||||||
try {
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, Config.HANDLER.instance().getRawKey(), Config.HANDLER.instance().getRawIv());
|
||||||
String strippedMessage = message_content.replace("®", "");
|
String strippedMessage = message_content.replace("®", "");
|
||||||
byte[] decodedMessage = strippedMessage.getBytes(java.nio.charset.StandardCharsets.UTF_16);
|
byte[] decodedMessage = ChatCoder.decodeFromBmp(strippedMessage);
|
||||||
byte[] unpaddedMessage = Arrays.copyOfRange(decodedMessage, 2, decodedMessage.length);
|
Cipher decryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
Cipher decryptingCipher = Cipher.getInstance("RSA");
|
decryptingCipher.update(decodedMessage);
|
||||||
decryptingCipher.init(Cipher.DECRYPT_MODE, privateKey);
|
|
||||||
decryptingCipher.update(unpaddedMessage);
|
|
||||||
String decryptedMessage = new String(decryptingCipher.doFinal());
|
String decryptedMessage = new String(decryptingCipher.doFinal());
|
||||||
String outputMessage = "{" + player_name + "} " + decryptedMessage;
|
String outputMessage = "{" + player_name + "} " + decryptedMessage;
|
||||||
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.of(outputMessage));
|
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.of(outputMessage));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException |
|
catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException |
|
||||||
InvalidKeyException e){
|
InvalidKeyException e){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -65,25 +52,29 @@ public class SecureChat implements ClientModInitializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String encryptChatMessage(String message) {
|
private String encryptChatMessage(String message) {
|
||||||
String encodedMessage;
|
if(Config.HANDLER.instance().isEnabled()){
|
||||||
try {
|
String encodedMessage;
|
||||||
Cipher encryptingCipher = Cipher.getInstance("RSA");
|
try {
|
||||||
encryptingCipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
encryptingCipher.update(message.getBytes(StandardCharsets.UTF_8));
|
cipher.init(Cipher.DECRYPT_MODE, Config.HANDLER.instance().getRawKey(), Config.HANDLER.instance().getRawIv());
|
||||||
byte[] encryptedMessage = encryptingCipher.doFinal();
|
cipher.update(message.getBytes(StandardCharsets.UTF_8));
|
||||||
encodedMessage = new String(encryptedMessage, java.nio.charset.StandardCharsets.UTF_16);
|
byte[] encryptedMessage = cipher.doFinal();
|
||||||
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | NoSuchPaddingException |
|
encodedMessage = ChatCoder.encodeToBmp(encryptedMessage);
|
||||||
NoSuchAlgorithmException e) {
|
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException |
|
||||||
throw new RuntimeException(e);
|
NoSuchAlgorithmException e) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
return '®' + encodedMessage + '®';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
return '®' + encodedMessage + '®';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
|
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
|
||||||
// Register event listener for ClientTickEvents.END_CLIENT_TICK
|
// Register event listener for ClientTickEvents.END_CLIENT_TICK
|
||||||
initKeys();
|
|
||||||
ClientReceiveMessageEvents.ALLOW_CHAT.register(this::decryptChatMessage);
|
ClientReceiveMessageEvents.ALLOW_CHAT.register(this::decryptChatMessage);
|
||||||
ClientSendMessageEvents.MODIFY_CHAT.register(this::encryptChatMessage);
|
ClientSendMessageEvents.MODIFY_CHAT.register(this::encryptChatMessage);
|
||||||
});
|
});
|
||||||
|
@ -17,11 +17,15 @@
|
|||||||
"entrypoints": {
|
"entrypoints": {
|
||||||
"client": [
|
"client": [
|
||||||
"systems.brn.chatencryptor.SecureChat"
|
"systems.brn.chatencryptor.SecureChat"
|
||||||
|
],
|
||||||
|
"modmenu": [
|
||||||
|
"systems.brn.chatencryptor.ModMenuIntegration"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=${loader_version}",
|
"fabricloader": ">=${loader_version}",
|
||||||
"fabric": "*",
|
"fabric": "*",
|
||||||
"minecraft": "${minecraft_version}"
|
"minecraft": "${minecraft_version}",
|
||||||
|
"yet_another_config_lib_v3": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Loading…
Reference in New Issue
Block a user