Makes one-time-pad and substitution cipher real encryption compatible
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good

This commit is contained in:
2025-08-18 18:02:57 +02:00
parent 6adec89ae1
commit cb70a8298d
10 changed files with 69 additions and 53 deletions

View File

@@ -34,10 +34,10 @@ public class CommandEncrypt implements TabExecutor {
return false; return false;
} }
EncryptionStyle encryptionStyle = arguments.length == 2 ? EncryptionStyle.getFromString(arguments[1]) : EncryptionStyle.SUBSTITUTION; EncryptionStyle encryptionStyle = arguments.length == 2 ? EncryptionStyle.getFromString(arguments[1]) : EncryptionStyle.AES;
// AES is the only reliable method for retaining the plaintext // AES is the only reliable method for retaining the plaintext
if (BooksWithoutBorders.getConfiguration().useRealEncryption()) { if (BooksWithoutBorders.getConfiguration().useRealEncryption() && !encryptionStyle.isRealEncryptionSupported()) {
encryptionStyle = EncryptionStyle.AES; encryptionStyle = EncryptionStyle.AES;
} }
@@ -132,10 +132,13 @@ public class CommandEncrypt implements TabExecutor {
@NotNull @NotNull
protected List<String> doTabCompletion(@NotNull String[] args, boolean groupEncrypt) { protected List<String> doTabCompletion(@NotNull String[] args, boolean groupEncrypt) {
int argumentsCount = args.length; int argumentsCount = args.length;
boolean useRealEncryption = BooksWithoutBorders.getConfiguration().useRealEncryption();
List<String> encryptionStyles = new ArrayList<>(); List<String> encryptionStyles = new ArrayList<>();
for (EncryptionStyle encryptionStyle : EncryptionStyle.values()) { for (EncryptionStyle encryptionStyle : EncryptionStyle.values()) {
encryptionStyles.add(encryptionStyle.toString()); if (!useRealEncryption || encryptionStyle.isRealEncryptionSupported()) {
encryptionStyles.add(encryptionStyle.toString());
}
} }
if (groupEncrypt) { if (groupEncrypt) {
@@ -150,9 +153,6 @@ public class CommandEncrypt implements TabExecutor {
if (argumentsCount == 1) { if (argumentsCount == 1) {
return List.of("<password>"); return List.of("<password>");
} else if (argumentsCount == 2) { } else if (argumentsCount == 2) {
if (BooksWithoutBorders.getConfiguration().useRealEncryption()) {
return List.of();
}
return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[1]); return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[1]);
} }
} }

View File

@@ -10,38 +10,53 @@ public enum EncryptionStyle {
/** /**
* Possibly lossy encryption using DNA codons * Possibly lossy encryption using DNA codons
*/ */
DNA("dna"), DNA("dna", false),
/** /**
* A simple cipher using the key to substitute one character for another * A simple cipher using the key to substitute one character for another
*/ */
SUBSTITUTION("substitution"), SUBSTITUTION("substitution", true),
/** /**
* A military-grade encryption cypher * A military-grade encryption cypher
*/ */
AES("aes"), AES("aes", true),
/** /**
* An unbreakable encryption method assuming the key is completely random and never used more than once, ever * An unbreakable encryption method assuming the key is completely random and never used more than once, ever
*/ */
ONE_TIME_PAD("onetimepad"), ONE_TIME_PAD("onetimepad", true),
/** /**
* Just a way of using magic text to make text illegible * Just a way of using magic text to make text illegible
*/ */
MAGIC("magic"), MAGIC("magic", false),
; ;
private final String name; private final String name;
private final boolean realEncryptionSupported;
/** /**
* Instantiates a new encryption style * Instantiates a new encryption style
* *
* @param name <p>The human-readable encryption style name</p> * @param name <p>The human-readable encryption style name</p>
* @param realEncryptionSupported <p>Whether the encryption style can be used for real encryption</p>
*/ */
EncryptionStyle(@NotNull String name) { EncryptionStyle(@NotNull String name, boolean realEncryptionSupported) {
this.name = name; this.name = name;
this.realEncryptionSupported = realEncryptionSupported;
}
/**
* Gets whether this encryption style supports real encryption
*
* <p>Real encryption means that only the cypher text is stored on disk, so the server owner cannot simply check the
* contents of encrypted books.</p>
*
* @return <p>True if this encryption style supports real encryption</p>
*/
public boolean isRealEncryptionSupported() {
return this.realEncryptionSupported;
} }
/** /**
@@ -57,7 +72,7 @@ public enum EncryptionStyle {
return style; return style;
} }
} }
return SUBSTITUTION; return AES;
} }
@Override @Override

View File

@@ -1,5 +1,6 @@
package net.knarcraft.bookswithoutborders.encryption; package net.knarcraft.bookswithoutborders.encryption;
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -26,12 +27,12 @@ public class OneTimePad implements Encryptor {
@Override @Override
public @Nullable String encryptText(@NotNull String input) { public @Nullable String encryptText(@NotNull String input) {
return oneTimePad(input); return oneTimePad(input, true);
} }
@Override @Override
public @Nullable String decryptText(@NotNull String input) { public @Nullable String decryptText(@NotNull String input) {
return oneTimePad(input); return oneTimePad(input, false);
} }
/** /**
@@ -40,11 +41,16 @@ public class OneTimePad implements Encryptor {
* <p>The one time pad encryption is very secure, and encryption works just like decryption, but is vulnerable if * <p>The one time pad encryption is very secure, and encryption works just like decryption, but is vulnerable if
* the same key is used more than once.</p> * the same key is used more than once.</p>
* *
* @param input <p>The input to encrypt/decrypt</p> * @param input <p>The input to encrypt/decrypt</p>
* @param encrypt <p>Whether to encrypt or decrypt the input</p>
* @return <p>The encrypted/decrypted output</p> * @return <p>The encrypted/decrypted output</p>
*/ */
@NotNull @NotNull
public String oneTimePad(@NotNull String input) { public String oneTimePad(@NotNull String input, boolean encrypt) {
if (!encrypt) {
input = new String(EncryptionHelper.hexStringToByteArray(input), StandardCharsets.UTF_8);
}
String longKey; String longKey;
try { try {
final MessageDigest digest = MessageDigest.getInstance("SHA3-256"); final MessageDigest digest = MessageDigest.getInstance("SHA3-256");
@@ -58,7 +64,12 @@ public class OneTimePad implements Encryptor {
for (int i = 0; i < input.length(); i++) { for (int i = 0; i < input.length(); i++) {
output.append((char) (input.charAt(i) ^ longKey.charAt(i % longKey.length()))); output.append((char) (input.charAt(i) ^ longKey.charAt(i % longKey.length())));
} }
return output.toString();
if (encrypt) {
return EncryptionHelper.bytesToHex(output.toString().getBytes(StandardCharsets.UTF_8));
} else {
return output.toString();
}
} }
} }

View File

@@ -1,9 +1,11 @@
package net.knarcraft.bookswithoutborders.encryption; package net.knarcraft.bookswithoutborders.encryption;
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.StringTokenizer; import java.util.StringTokenizer;
/** /**
@@ -49,6 +51,10 @@ public class SubstitutionCipher implements Encryptor {
return output.toString(); return output.toString();
} }
if (!encrypt) {
input = new String(EncryptionHelper.hexStringToByteArray(input), StandardCharsets.UTF_8);
}
// converts each number in the key to an integer and adds to an array // converts each number in the key to an integer and adds to an array
int[] offsetArray = getOffsetArray(this.key); int[] offsetArray = getOffsetArray(this.key);
@@ -68,7 +74,12 @@ public class SubstitutionCipher implements Encryptor {
offsetPosition = 0; offsetPosition = 0;
} }
} }
return output.toString();
if (encrypt) {
return EncryptionHelper.bytesToHex(output.toString().getBytes(StandardCharsets.UTF_8));
} else {
return output.toString();
}
} }
/** /**

View File

@@ -12,7 +12,7 @@ en:
SUCCESS_PAGE_DELETED: "Page deleted!" SUCCESS_PAGE_DELETED: "Page deleted!"
SUCCESS_BOOK_LOADED: "Book created!" SUCCESS_BOOK_LOADED: "Book created!"
SUCCESS_MIGRATION_STARTED: "Starting book migration..." SUCCESS_MIGRATION_STARTED: "Starting book migration..."
SUCCESS_RELOADED: "BooksWithoutBorders configuration reloaded!" SUCCESS_RELOADED: "Configuration, books and language strings reloaded!"
SUCCESS_SAVED: "Book Saved as &e\"{fileName}\"&r" SUCCESS_SAVED: "Book Saved as &e\"{fileName}\"&r"
ACTION_COPY: "copy" ACTION_COPY: "copy"
ACTION_CLEAR: "clear" ACTION_CLEAR: "clear"

View File

@@ -1,22 +0,0 @@
package net.knarcraft.bookswithoutborders;
import net.knarcraft.bookswithoutborders.encryption.GenenCrypt;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class GenenCryptTest {
@Test
public void encryptDecryptTest() {
GenenCrypt gc = new GenenCrypt("Another Key");
gc.printCodonTable();
String encrypted = gc.encryptText("Hello World!");
assertNotNull(encrypted);
assertEquals("HELLO WORLD!", gc.decryptText(encrypted));
}
}

View File

@@ -13,21 +13,16 @@ public class AESTest {
String plainText = "Flåklypa"; String plainText = "Flåklypa";
String password = "TqOZdpY9RjjjVE9JjCWVecUYObv5MYidByrpI3cxjoY="; String password = "TqOZdpY9RjjjVE9JjCWVecUYObv5MYidByrpI3cxjoY=";
System.out.println("Plaintext: " + plainText);
System.out.println("Encryption password: " + password);
AESConfiguration configuration = new AESConfiguration(new byte[]{-85, 103, -82, 71, 119, 28, 73, -75, -81, 102, -127, -125, -8, -75, 81, -111}, AESConfiguration configuration = new AESConfiguration(new byte[]{-85, 103, -82, 71, 119, 28, 73, -75, -81, 102, -127, -125, -8, -75, 81, -111},
new byte[]{(byte) 104, -42, 63, 31, -120, -2, 14, -119, 35, 122, 109, -64, 122, 117, 33, -85}, password); new byte[]{(byte) 104, -42, 63, 31, -120, -2, 14, -119, 35, 122, 109, -64, 122, 117, 33, -85}, password);
AES aes = new AES(configuration); AES aes = new AES(configuration);
String cypherText = aes.encryptText(plainText); String cypherText = aes.encryptText(plainText);
System.out.println("Cypher text: " + cypherText);
assertNotNull(cypherText); assertNotNull(cypherText);
assertNotEquals(plainText, cypherText); assertNotEquals(plainText, cypherText);
String decrypted = aes.decryptText(cypherText); String decrypted = aes.decryptText(cypherText);
System.out.println("Decrypted: " + decrypted);
assertEquals(plainText, decrypted); assertEquals(plainText, decrypted);
} }

View File

@@ -12,8 +12,12 @@ public class GenenCryptTest {
@Test @Test
public void encryptDecryptTest() { public void encryptDecryptTest() {
String encryptionKey = EncryptionHelper.getNumberKeyFromStringKey("My secret password!"); String encryptionKey = EncryptionHelper.getNumberKeyFromStringKey("My secret password!");
String plaintext = "Very secret &4colored&r message."; String plaintext = "Very secret &4colored&r message. That might be quite long. Of course, the length might " +
"cause problems, especially as the gene encryption requires several characters for every encrypted " +
"character. Also, the hexadecimal representation of the original text is encrypted. It is unknown if " +
"that might represent an increase in length.";
GenenCrypt genenCrypt = new GenenCrypt(encryptionKey); GenenCrypt genenCrypt = new GenenCrypt(encryptionKey);
genenCrypt.printCodonTable();
String cypherText = genenCrypt.encryptText(plaintext); String cypherText = genenCrypt.encryptText(plaintext);

View File

@@ -10,8 +10,9 @@ public class OneTimePadTest {
@Test @Test
public void oneTimePadTest() { public void oneTimePadTest() {
String plaintext = "Very secret text that should be kept secret"; String plaintext = "Very secret text that should be kept secret. It should be noted that several characters, " +
String key = "Very secret key!"; "like !\"#¤%&/()=?`§|@£${[]}'*,.-;:_<>µ need to be tested. Also foreign characters, like: øæåØÆÅ";
String key = "Very secret key that you will never guess!¤%&/";
OneTimePad oneTimePad = new OneTimePad(key); OneTimePad oneTimePad = new OneTimePad(key);
String cypherText = oneTimePad.encryptText(plaintext); String cypherText = oneTimePad.encryptText(plaintext);

View File

@@ -11,8 +11,9 @@ public class SubstitutionCipherTest {
@Test @Test
public void encryptDecryptTest() { public void encryptDecryptTest() {
String plaintext = "Very secret text that should be kept secret"; String plaintext = "Very secret text that should be kept secret. It should be noted that several characters, " +
String integerKey = EncryptionHelper.getNumberKeyFromStringKey("Very secret key!"); "like !\"#¤%&/()=?`§|@£${[]}'*,.-;:_<>µ need to be tested. Also foreign characters, like: øæåØÆÅ";
String integerKey = EncryptionHelper.getNumberKeyFromStringKey("Very secret key that you will never guess!¤%&/");
SubstitutionCipher substitutionCipher = new SubstitutionCipher(integerKey); SubstitutionCipher substitutionCipher = new SubstitutionCipher(integerKey);
String cypherText = substitutionCipher.encryptText(plaintext); String cypherText = substitutionCipher.encryptText(plaintext);