diff --git a/DIRECTORY.md b/DIRECTORY.md
index 9ebbd484376b..2b47ae08ba4a 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -597,6 +597,7 @@
* slidingwindow
* [LongestSubstringWithoutRepeatingCharacters](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java)
* [MaxSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java)
+ * [MinSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java)
* sorts
* [AdaptiveMergeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java)
* [BeadSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BeadSort.java)
@@ -1217,6 +1218,7 @@
* slidingwindow
* [LongestSubstringWithoutRepeatingCharactersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharactersTest.java)
* [MaxSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java)
+ * [MinSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java)
* sorts
* [AdaptiveMergeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java)
* [BeadSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BeadSortTest.java)
diff --git a/src/main/java/com/thealgorithms/ciphers/XORCipher.java b/src/main/java/com/thealgorithms/ciphers/XORCipher.java
index c4410d8c77ba..a612ccfbcdef 100644
--- a/src/main/java/com/thealgorithms/ciphers/XORCipher.java
+++ b/src/main/java/com/thealgorithms/ciphers/XORCipher.java
@@ -5,18 +5,46 @@
import java.util.HexFormat;
/**
- * A simple implementation of XOR cipher that, given a key, allows to encrypt and decrypt a plaintext.
+ * A simple implementation of the XOR cipher that allows both encryption and decryption
+ * using a given key. This cipher works by applying the XOR bitwise operation between
+ * the bytes of the input text and the corresponding bytes of the key (repeating the key
+ * if necessary).
*
- * @author lcsjunior
+ * Usage:
+ * - Encryption: Converts plaintext into a hexadecimal-encoded ciphertext.
+ * - Decryption: Converts the hexadecimal ciphertext back into plaintext.
+ *
+ * Characteristics:
+ * - Symmetric: The same key is used for both encryption and decryption.
+ * - Simple but vulnerable: XOR encryption is insecure for real-world cryptography,
+ * especially when the same key is reused.
+ *
+ * Example:
+ * Plaintext: "Hello!"
+ * Key: "key"
+ * Encrypted: "27090c03120b"
+ * Decrypted: "Hello!"
*
+ * Reference: XOR Cipher - Wikipedia
+ *
+ * @author lcsjunior
*/
public final class XORCipher {
+ // Default character encoding for string conversion
private static final Charset CS_DEFAULT = StandardCharsets.UTF_8;
private XORCipher() {
}
+ /**
+ * Applies the XOR operation between the input bytes and the key bytes.
+ * If the key is shorter than the input, it wraps around (cyclically).
+ *
+ * @param inputBytes The input byte array (plaintext or ciphertext).
+ * @param keyBytes The key byte array used for XOR operation.
+ * @return A new byte array containing the XOR result.
+ */
public static byte[] xor(final byte[] inputBytes, final byte[] keyBytes) {
byte[] outputBytes = new byte[inputBytes.length];
for (int i = 0; i < inputBytes.length; ++i) {
@@ -25,14 +53,40 @@ public static byte[] xor(final byte[] inputBytes, final byte[] keyBytes) {
return outputBytes;
}
+ /**
+ * Encrypts the given plaintext using the XOR cipher with the specified key.
+ * The result is a hexadecimal-encoded string representing the ciphertext.
+ *
+ * @param plainText The input plaintext to encrypt.
+ * @param key The encryption key.
+ * @throws IllegalArgumentException if the key is empty.
+ * @return A hexadecimal string representing the encrypted text.
+ */
public static String encrypt(final String plainText, final String key) {
+ if (key.isEmpty()) {
+ throw new IllegalArgumentException("Key must not be empty");
+ }
+
byte[] plainTextBytes = plainText.getBytes(CS_DEFAULT);
byte[] keyBytes = key.getBytes(CS_DEFAULT);
byte[] xorResult = xor(plainTextBytes, keyBytes);
return HexFormat.of().formatHex(xorResult);
}
+ /**
+ * Decrypts the given ciphertext (in hexadecimal format) using the XOR cipher
+ * with the specified key. The result is the original plaintext.
+ *
+ * @param cipherText The hexadecimal string representing the encrypted text.
+ * @param key The decryption key (must be the same as the encryption key).
+ * @throws IllegalArgumentException if the key is empty.
+ * @return The decrypted plaintext.
+ */
public static String decrypt(final String cipherText, final String key) {
+ if (key.isEmpty()) {
+ throw new IllegalArgumentException("Key must not be empty");
+ }
+
byte[] cipherBytes = HexFormat.of().parseHex(cipherText);
byte[] keyBytes = key.getBytes(CS_DEFAULT);
byte[] xorResult = xor(cipherBytes, keyBytes);
diff --git a/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java b/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java
index 15e27d5d6778..fdfe640cc19b 100644
--- a/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java
+++ b/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java
@@ -1,34 +1,85 @@
package com.thealgorithms.ciphers;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class XORCipherTest {
@Test
- void xorEncryptTest() {
- // given
+ void xorEncryptDecryptTest() {
String plaintext = "My t&xt th@t will be ençrypted...";
String key = "My ç&cret key!";
- // when
String cipherText = XORCipher.encrypt(plaintext, key);
+ String decryptedText = XORCipher.decrypt(cipherText, key);
- // then
- assertEquals("000000b7815e1752111c601f450e48211500a1c206061ca6d35212150d4429570eed", cipherText);
+ assertEquals("My t&xt th@t will be ençrypted...", decryptedText);
}
@Test
- void xorDecryptTest() {
- // given
- String cipherText = "000000b7815e1752111c601f450e48211500a1c206061ca6d35212150d4429570eed";
- String key = "My ç&cret key!";
+ void testEmptyPlaintext() {
+ String plaintext = "";
+ String key = "anykey";
+
+ String cipherText = XORCipher.encrypt(plaintext, key);
+ String decryptedText = XORCipher.decrypt(cipherText, key);
+
+ assertEquals("", cipherText);
+ assertEquals("", decryptedText);
+ }
+
+ @Test
+ void testEmptyKey() {
+ String plaintext = "Hello World!";
+ String key = "";
+
+ assertThrows(IllegalArgumentException.class, () -> XORCipher.encrypt(plaintext, key));
+ assertThrows(IllegalArgumentException.class, () -> XORCipher.decrypt(plaintext, key));
+ }
+
+ @Test
+ void testShortKey() {
+ String plaintext = "Short message";
+ String key = "k";
+
+ String cipherText = XORCipher.encrypt(plaintext, key);
+ String decryptedText = XORCipher.decrypt(cipherText, key);
+
+ assertEquals(plaintext, decryptedText);
+ }
- // when
- String plainText = XORCipher.decrypt(cipherText, key);
+ @Test
+ void testNonASCIICharacters() {
+ String plaintext = "こんにちは世界"; // "Hello World" in Japanese (Konichiwa Sekai)
+ String key = "key";
+
+ String cipherText = XORCipher.encrypt(plaintext, key);
+ String decryptedText = XORCipher.decrypt(cipherText, key);
+
+ assertEquals(plaintext, decryptedText);
+ }
+
+ @Test
+ void testSameKeyAndPlaintext() {
+ String plaintext = "samekey";
+ String key = "samekey";
+
+ String cipherText = XORCipher.encrypt(plaintext, key);
+ String decryptedText = XORCipher.decrypt(cipherText, key);
+
+ assertEquals(plaintext, decryptedText);
+ }
+
+ @Test
+ void testLongPlaintextShortKey() {
+ String plaintext = "This is a long plaintext message.";
+ String key = "key";
+
+ String cipherText = XORCipher.encrypt(plaintext, key);
+ String decryptedText = XORCipher.decrypt(cipherText, key);
- // then
- assertEquals("My t&xt th@t will be ençrypted...", plainText);
+ assertEquals(plaintext, decryptedText);
}
}