Adds a per-book choice for preventing admin decryption when using real encryption
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
This commit is contained in:
71
README.md
71
README.md
@@ -35,6 +35,9 @@ Books without Borders has got your back!
|
|||||||
- Easily add a title page or chapter page (for an unsigned book, you can add a blank page as well)
|
- Easily add a title page or chapter page (for an unsigned book, you can add a blank page as well)
|
||||||
with `/addBookTitlePage`.
|
with `/addBookTitlePage`.
|
||||||
- Remove extra blank pages or unneeded chapter pages with `/deleteBookPage`
|
- Remove extra blank pages or unneeded chapter pages with `/deleteBookPage`
|
||||||
|
- If the necessary options are enabled, books can be truly encrypted with the AES cipher, making them impossible to
|
||||||
|
decrypt without knowing the password. Even admin decryption can be made useless. Only enable if you are aware of the
|
||||||
|
potential loss caused by forgotten passwords!
|
||||||
|
|
||||||
#### Group encryption
|
#### Group encryption
|
||||||
|
|
||||||
@@ -101,38 +104,39 @@ An in-game description of available commands is available through the /bwb comma
|
|||||||
|
|
||||||
#### Single permissions
|
#### Single permissions
|
||||||
|
|
||||||
| Node | Description |
|
| Node | Description |
|
||||||
|--------------------------------------------|---------------------------------------------------------------------------------|
|
|--------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| bookswithoutborders.addtitlepage | Allows player to add a blank title page to a book |
|
| bookswithoutborders.addtitlepage | Allows player to add a blank title page to a book |
|
||||||
| bookswithoutborders.bypassauthoronlycopy | Allows player to ignore Author_Only_Copy config setting |
|
| bookswithoutborders.bypassauthoronlycopy | Allows player to ignore Author_Only_Copy config setting |
|
||||||
| bookswithoutborders.bypassauthoronlyunsign | Allows player to ignore Author_Only_Unsign config setting |
|
| bookswithoutborders.bypassauthoronlyunsign | Allows player to ignore Author_Only_Unsign config setting |
|
||||||
| bookswithoutborders.bypassauthoronlysave | Allows player to ignore Author_Only_Save config setting |
|
| bookswithoutborders.bypassauthoronlysave | Allows player to ignore Author_Only_Save config setting |
|
||||||
| bookswithoutborders.bypassbookprice | Allows player to ignore Price_to_create_book config setting |
|
| bookswithoutborders.bypassbookprice | Allows player to ignore Price_to_create_book config setting |
|
||||||
| bookswithoutborders.clear | Allows player to clear the contents of the held writable book |
|
| bookswithoutborders.clear | Allows player to clear the contents of the held writable book |
|
||||||
| bookswithoutborders.copy | Allows player to copy books |
|
| bookswithoutborders.copy | Allows player to copy books |
|
||||||
| bookswithoutborders.decrypt | Allows player to decrypt books |
|
| bookswithoutborders.decrypt | Allows player to decrypt books |
|
||||||
| bookswithoutborders.decrypt.agroup | Allows player to decrypt books group-encrypted for group "agroup" |
|
| bookswithoutborders.decrypt.agroup | Allows player to decrypt books group-encrypted for group "agroup" |
|
||||||
| bookswithoutborders.delete | Allows player to delete books from their personal directory |
|
| bookswithoutborders.delete | Allows player to delete books from their personal directory |
|
||||||
| bookswithoutborders.deletepage | Allows player to delete a page from a book |
|
| bookswithoutborders.deletepage | Allows player to delete a page from a book |
|
||||||
| bookswithoutborders.editbookshelf | Allows player to set name/lore for bookshelves, used for peeking |
|
| bookswithoutborders.editbookshelf | Allows player to set name/lore for bookshelves, used for peeking |
|
||||||
| bookswithoutborders.encrypt | Allows player to encrypt books |
|
| bookswithoutborders.encrypt | Allows player to encrypt books |
|
||||||
| bookswithoutborders.format | Allows a player to format a book |
|
| bookswithoutborders.format | Allows a player to format a book |
|
||||||
| bookswithoutborders.give | Allows player to give another player one of their privately saved books |
|
| bookswithoutborders.give | Allows player to give another player one of their privately saved books |
|
||||||
| bookswithoutborders.givepublic | Allows a player to give another player a book from the public directory |
|
| bookswithoutborders.givepublic | Allows a player to give another player a book from the public directory |
|
||||||
| bookswithoutborders.groupencrypt | Allows player to use group-based encryption |
|
| bookswithoutborders.groupencrypt | Allows player to use group-based encryption |
|
||||||
| bookswithoutborders.load | Allows player to load books from their personal directory |
|
| bookswithoutborders.load | Allows player to load books from their personal directory |
|
||||||
| bookswithoutborders.loadpublic | Allows player to load from the public directory |
|
| bookswithoutborders.loadpublic | Allows player to load from the public directory |
|
||||||
| bookswithoutborders.peekbookshelf | Allows player to left-click a bookshelf to see the contents of the shelf |
|
| bookswithoutborders.peekbookshelf | Allows player to left-click a bookshelf to see the contents of the shelf |
|
||||||
| bookswithoutborders.reload | Allows player to reload this plugin |
|
| bookswithoutborders.reload | Allows player to reload this plugin |
|
||||||
| bookswithoutborders.save | Allows a player to save books to their personal directory |
|
| bookswithoutborders.save | Allows a player to save books to their personal directory |
|
||||||
| bookswithoutborders.savepublic | Allows player to save to the public directory |
|
| bookswithoutborders.savepublic | Allows player to save to the public directory |
|
||||||
| bookswithoutborders.setauthor | Allows player to set the author of the currently held book |
|
| bookswithoutborders.setauthor | Allows player to set the author of the currently held book |
|
||||||
| bookswithoutborders.setbookprice | Allows player to set the cost of creating a book |
|
| bookswithoutborders.setbookprice | Allows player to set the cost of creating a book |
|
||||||
| bookswithoutborders.setgeneration | Allows player to change the generation of a book (Original, Copy, Copy of Copy) |
|
| bookswithoutborders.setgeneration | Allows player to change the generation of a book (Original, Copy, Copy of Copy) |
|
||||||
| bookswithoutborders.settitle | Allows player to set the title of the currently held book |
|
| bookswithoutborders.settitle | Allows player to set the title of the currently held book |
|
||||||
| bookswithoutborders.signs | Allows player to create signs that give/encrypt/decrypt books |
|
| bookswithoutborders.signs | Allows player to create signs that give/encrypt/decrypt books |
|
||||||
| bookswithoutborders.unsign | Allows player to un-sign books |
|
| bookswithoutborders.unsign | Allows player to un-sign books |
|
||||||
| bookswithoutborders.setlore | Allows player to set the lore of the currently held item |
|
| bookswithoutborders.setlore | Allows player to set the lore of the currently held item |
|
||||||
|
| bookswithoutborders.preventadmindecryption | If use real encryption and prevent admin decryption options are enabled, allows player to disable admin decryption for a book |
|
||||||
|
|
||||||
### Signs
|
### Signs
|
||||||
|
|
||||||
@@ -172,4 +176,5 @@ The **_decrypt_** sign must have **\[Decrypt]** on its second line. The third li
|
|||||||
| Format_Book_After_Signing | Whether to automatically format every book when it's signed |
|
| Format_Book_After_Signing | Whether to automatically format every book when it's signed |
|
||||||
| Change_Generation_On_Copy | Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the vanilla behavior where a copy of a copy or tattered book cannot be copied further. |
|
| Change_Generation_On_Copy | Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the vanilla behavior where a copy of a copy or tattered book cannot be copied further. |
|
||||||
| Enable_Book_Peeking | Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents. |
|
| Enable_Book_Peeking | Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents. |
|
||||||
| Use_Real_Encryption | Enables true AES encryption instead of the very fake legacy encryption. The encryption key is stored in the book file to allow admin decryption, but looking at the encrypted book in the file system, only reveals the encrypted pages. Note that real encryption might alter, corrupt or lose a book's contents, so don't use real encryption with books that have no backup in in-game book form or saved book form. |
|
| Use_Real_Encryption | Enables true AES encryption instead of the very fake legacy encryption. The encryption key is stored in the book file to allow admin decryption, but looking at the encrypted book in the file system, only reveals the encrypted pages. Note that real encryption might alter, corrupt or lose a book's contents, so don't use real encryption with books that have no backup in in-game book form or saved book form. |
|
||||||
|
| Allow_Prevent_Admin_Decryption | Allows players to disable storing the encryption key for an encrypted book. This is only usable for real encryption. This effectively disable admin decryption for the book. Providing the correct password is the only way to decrypt a book without a stored key. THIS IS A DANGEROUS OPTION! |
|
@@ -1,6 +1,8 @@
|
|||||||
package net.knarcraft.bookswithoutborders.command;
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BwBConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.Permission;
|
||||||
import net.knarcraft.bookswithoutborders.config.translation.Translatable;
|
import net.knarcraft.bookswithoutborders.config.translation.Translatable;
|
||||||
import net.knarcraft.bookswithoutborders.encryption.EncryptionStyle;
|
import net.knarcraft.bookswithoutborders.encryption.EncryptionStyle;
|
||||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||||
@@ -29,7 +31,7 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
@NotNull String[] arguments) {
|
@NotNull String[] arguments) {
|
||||||
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
|
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
|
||||||
if (performPreChecks(sender, arguments, 1,
|
if (performPreChecks(sender, arguments, 1, 2,
|
||||||
stringFormatter.getUnFormattedColoredMessage(Translatable.ERROR_ENCRYPT_NO_KEY)) == null) {
|
stringFormatter.getUnFormattedColoredMessage(Translatable.ERROR_ENCRYPT_NO_KEY)) == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -37,11 +39,16 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
EncryptionStyle encryptionStyle = arguments.length == 2 ? EncryptionStyle.getFromString(arguments[1]) : EncryptionStyle.AES;
|
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() && !encryptionStyle.isRealEncryptionSupported()) {
|
BwBConfig config = BooksWithoutBorders.getConfiguration();
|
||||||
|
boolean realEncryption = config.useRealEncryption();
|
||||||
|
if (realEncryption && !encryptionStyle.isRealEncryptionSupported()) {
|
||||||
encryptionStyle = EncryptionStyle.AES;
|
encryptionStyle = EncryptionStyle.AES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return encryptBook(encryptionStyle, (Player) sender, arguments[0], "");
|
boolean preventAdminDecryption = realEncryption && config.allowPreventAdminDecryption() &&
|
||||||
|
sender.hasPermission(Permission.PREVENT_ADMIN_DECRYPTION.toString());
|
||||||
|
|
||||||
|
return encryptBook(encryptionStyle, (Player) sender, arguments[0], "", preventAdminDecryption);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +62,7 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected BookMeta performPreChecks(@NotNull CommandSender sender, @NotNull String[] arguments,
|
protected BookMeta performPreChecks(@NotNull CommandSender sender, @NotNull String[] arguments,
|
||||||
int necessaryArguments, @NotNull String missingArgumentsError) {
|
int necessaryArguments, int optionalArguments, @NotNull String missingArgumentsError) {
|
||||||
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
|
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
|
||||||
if (!(sender instanceof Player player)) {
|
if (!(sender instanceof Player player)) {
|
||||||
stringFormatter.displayErrorMessage(sender, Translatable.ERROR_PLAYER_ONLY);
|
stringFormatter.displayErrorMessage(sender, Translatable.ERROR_PLAYER_ONLY);
|
||||||
@@ -75,7 +82,7 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
stringFormatter.displayErrorMessage(player, missingArgumentsError);
|
stringFormatter.displayErrorMessage(player, missingArgumentsError);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (argumentCount > necessaryArguments + 1) {
|
if (argumentCount > necessaryArguments + optionalArguments) {
|
||||||
stringFormatter.displayErrorMessage(player, Translatable.ERROR_TOO_MANY_ARGUMENTS_COMMAND);
|
stringFormatter.displayErrorMessage(player, Translatable.ERROR_TOO_MANY_ARGUMENTS_COMMAND);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -96,16 +103,18 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
/**
|
/**
|
||||||
* Encrypts the given book
|
* Encrypts the given book
|
||||||
*
|
*
|
||||||
* @param encryptionStyle <p>The encryption style to use</p>
|
* @param encryptionStyle <p>The encryption style to use</p>
|
||||||
* @param player <p>The player encrypting the book</p>
|
* @param player <p>The player encrypting the book</p>
|
||||||
* @param key <p>The encryption key to use</p>
|
* @param key <p>The encryption key to use</p>
|
||||||
* @param group <p>The group to encrypt for</p>
|
* @param group <p>The group to encrypt for</p>
|
||||||
|
* @param preventAdminDecryption <p>Whether to prevent storage of a key that can be used for admin decryption</p>
|
||||||
* @return <p>True if the book was encrypted successfully</p>
|
* @return <p>True if the book was encrypted successfully</p>
|
||||||
*/
|
*/
|
||||||
protected boolean encryptBook(@NotNull EncryptionStyle encryptionStyle, @NotNull Player player, @NotNull String key,
|
protected boolean encryptBook(@NotNull EncryptionStyle encryptionStyle, @NotNull Player player, @NotNull String key,
|
||||||
@NotNull String group) {
|
@NotNull String group, boolean preventAdminDecryption) {
|
||||||
ItemSlot heldSlot = InventoryHelper.getHeldSlotBook(player, false, false, true, true);
|
ItemSlot heldSlot = InventoryHelper.getHeldSlotBook(player, false, false, true, true);
|
||||||
ItemStack encryptedBook = EncryptionHelper.encryptBook(player, heldSlot == ItemSlot.MAIN_HAND, key, encryptionStyle, group);
|
ItemStack encryptedBook = EncryptionHelper.encryptBook(player, heldSlot == ItemSlot.MAIN_HAND, key,
|
||||||
|
encryptionStyle, group, preventAdminDecryption);
|
||||||
|
|
||||||
if (encryptedBook != null) {
|
if (encryptedBook != null) {
|
||||||
InventoryHelper.setHeldWrittenBook(player, encryptedBook);
|
InventoryHelper.setHeldWrittenBook(player, encryptedBook);
|
||||||
@@ -119,19 +128,20 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
@NotNull String[] arguments) {
|
@NotNull String[] arguments) {
|
||||||
return doTabCompletion(arguments, false);
|
return doTabCompletion(sender, arguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list of string for tab completions
|
* Gets a list of string for tab completions
|
||||||
*
|
*
|
||||||
* @param args <p>The arguments given</p>
|
* @param sender <p>The command sender executing this command</p>
|
||||||
|
* @param arguments <p>The arguments given</p>
|
||||||
* @param groupEncrypt <p>Whether to auto-complete for group encryption</p>
|
* @param groupEncrypt <p>Whether to auto-complete for group encryption</p>
|
||||||
* @return <p>The strings to auto-complete</p>
|
* @return <p>The strings to auto-complete</p>
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
protected List<String> doTabCompletion(@NotNull String[] args, boolean groupEncrypt) {
|
protected List<String> doTabCompletion(@NotNull CommandSender sender, @NotNull String[] arguments, boolean groupEncrypt) {
|
||||||
int argumentsCount = args.length;
|
int argumentsCount = arguments.length;
|
||||||
boolean useRealEncryption = BooksWithoutBorders.getConfiguration().useRealEncryption();
|
boolean useRealEncryption = BooksWithoutBorders.getConfiguration().useRealEncryption();
|
||||||
|
|
||||||
List<String> encryptionStyles = new ArrayList<>();
|
List<String> encryptionStyles = new ArrayList<>();
|
||||||
@@ -147,13 +157,17 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
} else if (argumentsCount == 2) {
|
} else if (argumentsCount == 2) {
|
||||||
return List.of("<password>");
|
return List.of("<password>");
|
||||||
} else if (argumentsCount == 3) {
|
} else if (argumentsCount == 3) {
|
||||||
return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[2]);
|
return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, arguments[2]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
BwBConfig config = BooksWithoutBorders.getConfiguration();
|
||||||
if (argumentsCount == 1) {
|
if (argumentsCount == 1) {
|
||||||
return List.of("<password>");
|
return List.of("<password>");
|
||||||
} else if (argumentsCount == 2) {
|
} else if (argumentsCount == 2) {
|
||||||
return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[1]);
|
return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, arguments[1]);
|
||||||
|
} else if (argumentsCount == 3 && (config.useRealEncryption() && config.allowPreventAdminDecryption()) &&
|
||||||
|
sender.hasPermission(Permission.PREVENT_ADMIN_DECRYPTION.toString())) {
|
||||||
|
return TabCompletionHelper.filterMatchingStartsWith(List.of("true", "false"), arguments[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return List.of();
|
return List.of();
|
||||||
|
@@ -27,7 +27,7 @@ public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BookMeta bookMetadata = performPreChecks(sender, arguments, 2,
|
BookMeta bookMetadata = performPreChecks(sender, arguments, 2, 1,
|
||||||
stringFormatter.getUnFormattedColoredMessage(Translatable.ERROR_GROUP_ENCRYPT_ARGUMENTS_MISSING));
|
stringFormatter.getUnFormattedColoredMessage(Translatable.ERROR_GROUP_ENCRYPT_ARGUMENTS_MISSING));
|
||||||
|
|
||||||
if (bookMetadata == null) {
|
if (bookMetadata == null) {
|
||||||
@@ -42,13 +42,13 @@ public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EncryptionStyle encryptionStyle = arguments.length == 3 ? EncryptionStyle.getFromString(arguments[2]) : EncryptionStyle.SUBSTITUTION;
|
EncryptionStyle encryptionStyle = arguments.length == 3 ? EncryptionStyle.getFromString(arguments[2]) : EncryptionStyle.SUBSTITUTION;
|
||||||
return encryptBook(encryptionStyle, player, arguments[1], arguments[0]);
|
return encryptBook(encryptionStyle, player, arguments[1], arguments[0], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
public @NotNull List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||||
@NotNull String alias, @NotNull String[] arguments) {
|
@NotNull String alias, @NotNull String[] arguments) {
|
||||||
return doTabCompletion(arguments, true);
|
return doTabCompletion(sender, arguments, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,7 @@ public class BwBConfig {
|
|||||||
private boolean changeGenerationOnCopy;
|
private boolean changeGenerationOnCopy;
|
||||||
private boolean enableBookshelfPeeking;
|
private boolean enableBookshelfPeeking;
|
||||||
private boolean useRealEncryption;
|
private boolean useRealEncryption;
|
||||||
|
private boolean allowPreventAdminDecryption;
|
||||||
|
|
||||||
private final Translator translator;
|
private final Translator translator;
|
||||||
private EconomyManager economyManager;
|
private EconomyManager economyManager;
|
||||||
@@ -275,6 +276,15 @@ public class BwBConfig {
|
|||||||
return this.useRealEncryption;
|
return this.useRealEncryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether to allow removing the admin decrypt ability
|
||||||
|
*
|
||||||
|
* @return <p>True if admin decrypt removal is allowed</p>
|
||||||
|
*/
|
||||||
|
public boolean allowPreventAdminDecryption() {
|
||||||
|
return this.allowPreventAdminDecryption;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the path used to store encrypted books
|
* Gets the path used to store encrypted books
|
||||||
*
|
*
|
||||||
@@ -316,6 +326,7 @@ public class BwBConfig {
|
|||||||
config.set(ConfigOption.AUTHOR_ONLY_UNSIGN.getConfigNode(), this.authorOnlyUnsign);
|
config.set(ConfigOption.AUTHOR_ONLY_UNSIGN.getConfigNode(), this.authorOnlyUnsign);
|
||||||
config.set(ConfigOption.AUTHOR_ONLY_SAVE.getConfigNode(), this.authorOnlySave);
|
config.set(ConfigOption.AUTHOR_ONLY_SAVE.getConfigNode(), this.authorOnlySave);
|
||||||
config.set(ConfigOption.CHANGE_GENERATION_ON_COPY.getConfigNode(), this.changeGenerationOnCopy);
|
config.set(ConfigOption.CHANGE_GENERATION_ON_COPY.getConfigNode(), this.changeGenerationOnCopy);
|
||||||
|
config.set(ConfigOption.ALLOW_PREVENT_ADMIN_DECRYPTION.getConfigNode(), this.allowPreventAdminDecryption);
|
||||||
BooksWithoutBorders.getInstance().saveConfig();
|
BooksWithoutBorders.getInstance().saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,6 +354,7 @@ public class BwBConfig {
|
|||||||
this.changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
this.changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
||||||
this.enableBookshelfPeeking = getBoolean(config, ConfigOption.ENABLE_BOOKSHELF_PEEKING);
|
this.enableBookshelfPeeking = getBoolean(config, ConfigOption.ENABLE_BOOKSHELF_PEEKING);
|
||||||
this.useRealEncryption = getBoolean(config, ConfigOption.USE_REAL_ENCRYPTION);
|
this.useRealEncryption = getBoolean(config, ConfigOption.USE_REAL_ENCRYPTION);
|
||||||
|
this.allowPreventAdminDecryption = getBoolean(config, ConfigOption.ALLOW_PREVENT_ADMIN_DECRYPTION);
|
||||||
String language = config.getString("language", "en");
|
String language = config.getString("language", "en");
|
||||||
this.translator.loadLanguages(BooksWithoutBorders.getInstance().getDataFolder(), "en", language);
|
this.translator.loadLanguages(BooksWithoutBorders.getInstance().getDataFolder(), "en", language);
|
||||||
|
|
||||||
|
@@ -81,6 +81,11 @@ public enum ConfigOption {
|
|||||||
* Whether to use real AES encryption instead of storing garbled book text, while the full plaintext is stored in a file
|
* Whether to use real AES encryption instead of storing garbled book text, while the full plaintext is stored in a file
|
||||||
*/
|
*/
|
||||||
USE_REAL_ENCRYPTION("Options.Use_Real_Encryption", false),
|
USE_REAL_ENCRYPTION("Options.Use_Real_Encryption", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to allow disabling admin encryption for a real encrypted book
|
||||||
|
*/
|
||||||
|
ALLOW_PREVENT_ADMIN_DECRYPTION("Options.Allow_Prevent_Admin_Decryption", false),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String configNode;
|
private final String configNode;
|
||||||
|
@@ -50,7 +50,13 @@ public enum Permission {
|
|||||||
/**
|
/**
|
||||||
* The permission for using special signs
|
* The permission for using special signs
|
||||||
*/
|
*/
|
||||||
SIGNS("signs");
|
SIGNS("signs"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The permission for preventing a real encrypted book to be decrypted by an admin
|
||||||
|
*/
|
||||||
|
PREVENT_ADMIN_DECRYPTION("preventAdminDecryption"),
|
||||||
|
;
|
||||||
|
|
||||||
private final @NotNull String node;
|
private final @NotNull String node;
|
||||||
|
|
||||||
|
@@ -11,13 +11,14 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* A representation of an encrypted book
|
* A representation of an encrypted book
|
||||||
*
|
*
|
||||||
* @param bookMeta <p>The book's book meta</p>
|
* @param bookMeta <p>The book's book meta</p>
|
||||||
* @param encryptionStyle <p>The book's encryption style</p>
|
* @param encryptionStyle <p>The book's encryption style</p>
|
||||||
* @param encryptionKey <p>The book's encryption key, or null if admin decrypt is forbidden</p>
|
* @param encryptionKey <p>The book's encryption key, or null if admin decrypt is forbidden</p>
|
||||||
* @param data <p>The encrypted pages of the book</p>
|
* @param data <p>The encrypted pages of the book</p>
|
||||||
* @param aesConfiguration <p>The AES configuration for the book, or null if not AES encrypted</p>
|
* @param aesConfiguration <p>The AES configuration for the book, or null if not AES encrypted</p>
|
||||||
|
* @param preventAdminDecrypt <p>Whether this book should not support admin decryption</p>
|
||||||
*/
|
*/
|
||||||
public record EncryptedBook(@NotNull BookMeta bookMeta, @NotNull EncryptionStyle encryptionStyle,
|
public record EncryptedBook(@NotNull BookMeta bookMeta, @NotNull EncryptionStyle encryptionStyle,
|
||||||
@NotNull String encryptionKey, @NotNull List<String> data,
|
@NotNull String encryptionKey, @NotNull List<String> data,
|
||||||
@Nullable AESConfiguration aesConfiguration) {
|
@Nullable AESConfiguration aesConfiguration, boolean preventAdminDecrypt) {
|
||||||
}
|
}
|
||||||
|
@@ -304,7 +304,7 @@ public class SignEventListener implements Listener {
|
|||||||
if (heldItemType == Material.WRITTEN_BOOK) {
|
if (heldItemType == Material.WRITTEN_BOOK) {
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
eBook = EncryptionHelper.encryptBook(player, mainHand, BookFormatter.stripColor(lines[2]),
|
eBook = EncryptionHelper.encryptBook(player, mainHand, BookFormatter.stripColor(lines[2]),
|
||||||
EncryptionStyle.getFromString(BookFormatter.stripColor(lines[3])));
|
EncryptionStyle.getFromString(BookFormatter.stripColor(lines[3])), false);
|
||||||
if (eBook != null) {
|
if (eBook != null) {
|
||||||
player.getInventory().setItem(hand, eBook);
|
player.getInventory().setItem(hand, eBook);
|
||||||
}
|
}
|
||||||
|
@@ -76,7 +76,9 @@ public final class BookToFromTextHelper {
|
|||||||
FileConfiguration bookYml = getBookConfiguration(encryptedBook.bookMeta());
|
FileConfiguration bookYml = getBookConfiguration(encryptedBook.bookMeta());
|
||||||
|
|
||||||
bookYml.set("Encryption.Style", encryptedBook.encryptionStyle().toString());
|
bookYml.set("Encryption.Style", encryptedBook.encryptionStyle().toString());
|
||||||
bookYml.set("Encryption.Key", encryptedBook.encryptionKey());
|
if (!encryptedBook.preventAdminDecrypt()) {
|
||||||
|
bookYml.set("Encryption.Key", encryptedBook.encryptionKey());
|
||||||
|
}
|
||||||
if (encryptedBook.encryptionStyle() == EncryptionStyle.AES) {
|
if (encryptedBook.encryptionStyle() == EncryptionStyle.AES) {
|
||||||
if (encryptedBook.aesConfiguration() == null) {
|
if (encryptedBook.aesConfiguration() == null) {
|
||||||
throw new IOException("Attempted to save AES encrypted book without supplying a configuration!");
|
throw new IOException("Attempted to save AES encrypted book without supplying a configuration!");
|
||||||
@@ -155,7 +157,8 @@ public final class BookToFromTextHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the plaintext is stored in the file, don't bother with real decryption
|
// If the plaintext is stored in the file, don't bother with real decryption
|
||||||
EncryptedBook encryptedBook = new EncryptedBook(meta, encryptionStyle, userKey, data, aesConfiguration);
|
EncryptedBook encryptedBook = new EncryptedBook(meta, encryptionStyle, userKey, data, aesConfiguration,
|
||||||
|
realKey.isBlank());
|
||||||
if (!meta.getPages().isEmpty()) {
|
if (!meta.getPages().isEmpty()) {
|
||||||
return encryptedBook;
|
return encryptedBook;
|
||||||
}
|
}
|
||||||
|
@@ -127,31 +127,34 @@ public final class EncryptionHelper {
|
|||||||
/**
|
/**
|
||||||
* Encrypts a book
|
* Encrypts a book
|
||||||
*
|
*
|
||||||
* @param player <p>The player encrypting the book</p>
|
* @param player <p>The player encrypting the book</p>
|
||||||
* @param mainHand <p>Whether the player is holding the book in its main hand</p>
|
* @param mainHand <p>Whether the player is holding the book in its main hand</p>
|
||||||
* @param key <p>The key/password to use for encryption</p>
|
* @param key <p>The key/password to use for encryption</p>
|
||||||
* @param style <p>The encryption style to use</p>
|
* @param style <p>The encryption style to use</p>
|
||||||
|
* @param preventAdminDecrypt <p>Whether to prevent storage of a key that can be used for admin decryption</p>
|
||||||
* @return <p>An encrypted version of the book</p>
|
* @return <p>An encrypted version of the book</p>
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static ItemStack encryptBook(@NotNull Player player, boolean mainHand, @NotNull String key,
|
public static ItemStack encryptBook(@NotNull Player player, boolean mainHand, @NotNull String key,
|
||||||
@NotNull EncryptionStyle style) {
|
@NotNull EncryptionStyle style, boolean preventAdminDecrypt) {
|
||||||
return encryptBook(player, mainHand, key, style, "");
|
return encryptBook(player, mainHand, key, style, "", preventAdminDecrypt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts a book
|
* Encrypts a book
|
||||||
*
|
*
|
||||||
* @param player <p>The player encrypting the book</p>
|
* @param player <p>The player encrypting the book</p>
|
||||||
* @param mainHand <p>Whether the player is holding the book in its main hand</p>
|
* @param mainHand <p>Whether the player is holding the book in its main hand</p>
|
||||||
* @param key <p>The key/password to use for encryption</p>
|
* @param key <p>The key/password to use for encryption</p>
|
||||||
* @param style <p>The encryption style to use</p>
|
* @param style <p>The encryption style to use</p>
|
||||||
* @param groupName <p>The name of the group to encrypt for, or "" otherwise</p>
|
* @param groupName <p>The name of the group to encrypt for, or "" otherwise</p>
|
||||||
|
* @param preventAdminDecrypt <p>Whether to prevent storage of a key that can be used for admin decryption</p>
|
||||||
* @return <p>An encrypted version of the book</p>
|
* @return <p>An encrypted version of the book</p>
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static ItemStack encryptBook(Player player, boolean mainHand, @NotNull String key,
|
public static ItemStack encryptBook(Player player, boolean mainHand, @NotNull String key,
|
||||||
@NotNull EncryptionStyle style, @NotNull String groupName) {
|
@NotNull EncryptionStyle style, @NotNull String groupName,
|
||||||
|
boolean preventAdminDecrypt) {
|
||||||
BookMeta book = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
BookMeta book = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
||||||
if (book == null) {
|
if (book == null) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata from the held book!");
|
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata from the held book!");
|
||||||
@@ -167,7 +170,8 @@ public final class EncryptionHelper {
|
|||||||
AESConfiguration configuration = AESConfiguration.getNewConfiguration(hashedKey);
|
AESConfiguration configuration = AESConfiguration.getNewConfiguration(hashedKey);
|
||||||
|
|
||||||
//Save the book's un-encrypted contents to a file
|
//Save the book's un-encrypted contents to a file
|
||||||
BookMeta newMetadata = saveBookPlaintext(groupName, player, book, style, hashedKey, configuration);
|
BookMeta newMetadata = saveEncryptedBook(groupName, player,
|
||||||
|
new EncryptedBook(book, style, hashedKey, new ArrayList<>(), configuration, preventAdminDecrypt));
|
||||||
if (newMetadata == null) {
|
if (newMetadata == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -216,26 +220,22 @@ public final class EncryptionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a book's plain text to a file
|
* Saves an encrypted book to a file
|
||||||
*
|
*
|
||||||
* @param groupName <p>The group who's allowed to decrypt the book, or ""</p>
|
* @param groupName <p>The group who's allowed to decrypt the book, or ""</p>
|
||||||
* @param player <p>The player trying to encrypt the book</p>
|
* @param player <p>The player trying to encrypt the book</p>
|
||||||
* @param book <p>The book to encrypt</p>
|
* @param encryptedBook <p>The book to encrypt</p>
|
||||||
* @param encryptionStyle <p>The encryption style used for the book</p>
|
|
||||||
* @param key <p>The key used to encrypt the book</p>
|
|
||||||
* @param aesConfiguration <p>The AES configuration to use, if encrypting using AES</p>
|
|
||||||
* @return <p>The new metadata for the book, or null if it could not be saved</p>
|
* @return <p>The new metadata for the book, or null if it could not be saved</p>
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static BookMeta saveBookPlaintext(@NotNull String groupName, @NotNull Player player,
|
private static BookMeta saveEncryptedBook(@NotNull String groupName, @NotNull Player player,
|
||||||
@NotNull BookMeta book, @NotNull EncryptionStyle encryptionStyle,
|
@NotNull EncryptedBook encryptedBook) {
|
||||||
@NotNull String key, @NotNull AESConfiguration aesConfiguration) {
|
BookMeta newMetadata = encryptedBook.bookMeta();
|
||||||
BookMeta newMetadata = book;
|
|
||||||
boolean wasSaved;
|
boolean wasSaved;
|
||||||
if (groupName.trim().isEmpty()) {
|
if (groupName.trim().isEmpty()) {
|
||||||
wasSaved = saveEncryptedBook(player, book, encryptionStyle, key, aesConfiguration);
|
wasSaved = saveEncryptedBook(player, encryptedBook);
|
||||||
} else {
|
} else {
|
||||||
newMetadata = saveEncryptedBookForGroup(player, book, groupName);
|
newMetadata = saveEncryptedBookForGroup(player, encryptedBook.bookMeta(), groupName);
|
||||||
wasSaved = newMetadata != null;
|
wasSaved = newMetadata != null;
|
||||||
}
|
}
|
||||||
if (wasSaved) {
|
if (wasSaved) {
|
||||||
@@ -327,7 +327,6 @@ public final class EncryptionHelper {
|
|||||||
File file = new File(path + fileName + ".yml");
|
File file = new File(path + fileName + ".yml");
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
file = new File(path + fileName + ".txt");
|
file = new File(path + fileName + ".txt");
|
||||||
|
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Incorrect decryption key!");
|
BooksWithoutBorders.sendErrorMessage(player, "Incorrect decryption key!");
|
||||||
return null;
|
return null;
|
||||||
@@ -476,20 +475,15 @@ public final class EncryptionHelper {
|
|||||||
/**
|
/**
|
||||||
* Saves an encrypted book to be decryptable for the given user
|
* Saves an encrypted book to be decryptable for the given user
|
||||||
*
|
*
|
||||||
* @param player <p>The player encrypting the book</p>
|
* @param player <p>The player encrypting the book</p>
|
||||||
* @param bookMetaData <p>Metadata for the book to encrypt</p>
|
* @param encryptedBook <p>The book to save</p>
|
||||||
* @param encryptionStyle <p>The style of encryption used</p>
|
|
||||||
* @param key <p>The key to use for encryption</p>
|
|
||||||
* @param aesConfiguration <p>The AES configuration to use if encrypting with AES</p>
|
|
||||||
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
|
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
private static Boolean saveEncryptedBook(@NotNull Player player, @NotNull BookMeta bookMetaData,
|
private static Boolean saveEncryptedBook(@NotNull Player player, @NotNull EncryptedBook encryptedBook) {
|
||||||
@NotNull EncryptionStyle encryptionStyle, @NotNull String key,
|
|
||||||
@Nullable AESConfiguration aesConfiguration) {
|
|
||||||
String path = BooksWithoutBorders.getConfiguration().getEncryptedBookPath();
|
String path = BooksWithoutBorders.getConfiguration().getEncryptedBookPath();
|
||||||
|
|
||||||
String fileName = BookHelper.getBookFile(bookMetaData, player, true);
|
String fileName = BookHelper.getBookFile(encryptedBook.bookMeta(), player, true);
|
||||||
fileName = cleanString(fileName);
|
fileName = cleanString(fileName);
|
||||||
|
|
||||||
//cancels saving if file is already encrypted
|
//cancels saving if file is already encrypted
|
||||||
@@ -500,8 +494,7 @@ public final class EncryptionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BookToFromTextHelper.encryptedBookToYml(path, fileName,
|
BookToFromTextHelper.encryptedBookToYml(path, fileName, encryptedBook);
|
||||||
new EncryptedBook(bookMetaData, encryptionStyle, key, new ArrayList<>(), aesConfiguration));
|
|
||||||
} catch (IOException exception) {
|
} catch (IOException exception) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Encryption failed!");
|
BooksWithoutBorders.sendErrorMessage(player, "Encryption failed!");
|
||||||
return false;
|
return false;
|
||||||
|
@@ -38,4 +38,8 @@ Options:
|
|||||||
# peek at books, if an admin gets a hold of a book with the same title and author, but only the encrypted AES cypher text is stored in the book.
|
# peek at books, if an admin gets a hold of a book with the same title and author, but only the encrypted AES cypher text is stored in the book.
|
||||||
# Note that real encryption might alter, corrupt or lose a book's contents, so don't use real encryption with books
|
# Note that real encryption might alter, corrupt or lose a book's contents, so don't use real encryption with books
|
||||||
# that have no backup in in-game book form or saved book form.
|
# that have no backup in in-game book form or saved book form.
|
||||||
Use_Real_Encryption: false
|
Use_Real_Encryption: false
|
||||||
|
# Whether to allow players to specifically disable admin decryption for a real encrypted book. This is only available
|
||||||
|
# when real encryption is enabled. It allows a player to prevent the storage of the encryption key in the plugin
|
||||||
|
# folder, meaning that the only way to decrypt the book is to provide the correct key. THIS IS A DANGEROUS OPTION!
|
||||||
|
Allow_Prevent_Admin_Decryption: false
|
@@ -102,9 +102,11 @@ commands:
|
|||||||
Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces.
|
Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces.
|
||||||
"style" is not required. Possible values are "dna", "substitution", "aes", "onetimepad" and "magic".
|
"style" is not required. Possible values are "dna", "substitution", "aes", "onetimepad" and "magic".
|
||||||
If real encryption is enabled, possible methods are restricted.
|
If real encryption is enabled, possible methods are restricted.
|
||||||
|
If real encryption and prevent admin decryption are enabled, the third argument prevents the key from being
|
||||||
|
stored in the server files, preventing admin decryption. If the password is lost, decryption is impossible.
|
||||||
aliases:
|
aliases:
|
||||||
- bwbencrypt
|
- bwbencrypt
|
||||||
usage: /<command> <key> [encryption style]
|
usage: /<command> <key> [encryption style] [prevent admin decrypt]
|
||||||
permission: bookswithoutborders.encrypt
|
permission: bookswithoutborders.encrypt
|
||||||
setbookgeneration:
|
setbookgeneration:
|
||||||
description: Sets the generation of your held book
|
description: Sets the generation of your held book
|
||||||
@@ -265,6 +267,7 @@ permissions:
|
|||||||
bookswithoutborders.reload: true
|
bookswithoutborders.reload: true
|
||||||
bookswithoutborders.setgeneration: true
|
bookswithoutborders.setgeneration: true
|
||||||
bookswithoutborders.editbookshelf: true
|
bookswithoutborders.editbookshelf: true
|
||||||
|
bookswithoutborders.preventadmindecryption: true
|
||||||
bookswithoutborders.use:
|
bookswithoutborders.use:
|
||||||
description: Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled
|
description: Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled
|
||||||
children:
|
children:
|
||||||
@@ -341,4 +344,6 @@ permissions:
|
|||||||
bookswithoutborders.addtitlepage:
|
bookswithoutborders.addtitlepage:
|
||||||
description: Allows player to add a blank title page to a book
|
description: Allows player to add a blank title page to a book
|
||||||
bookswithoutborders.deletepage:
|
bookswithoutborders.deletepage:
|
||||||
description: Allows player to delete a page from a book
|
description: Allows player to delete a page from a book
|
||||||
|
bookswithoutborders.preventadmindecryption:
|
||||||
|
description: If use real encryption and prevent admin decryption options are enabled, allows player to disable admin decryption for a book
|
Reference in New Issue
Block a user