diff --git a/build.gradle.kts b/build.gradle.kts index fc06dfa..f125217 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ plugins { signing jacoco alias(libs.plugins.net.thebugmc.gradle.sonatype.central.portal.publisher) + alias(libs.plugins.me.champeau.jmh) } group = providers.gradleProperty("GROUP_NAME").get() @@ -107,4 +108,14 @@ centralPortal { developerConnection = providers.gradleProperty("POM_SCM_DEV_CONNECTION").get() } } +} + +jmh { + warmupIterations = 3 + iterations = 5 + fork = 1 + benchmarkMode = listOf("thrpt") + timeUnit = "s" + // 벤치마크 결과 리포트 형식 + resultFormat = "JSON" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index c4fd4d6..1153099 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ GROUP_NAME=io.github.ppzxc ARTIFACT_NAME=crypto -VERSION_NAME=0.0.43 +VERSION_NAME=0.0.44 VERSION_CODE=1 POM_NAME=Crypto Wrapper diff --git a/libs.versions.toml b/libs.versions.toml index 9c3f5cd..c6aafca 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -4,6 +4,7 @@ io-github-ppzxc-fixh = "0.0.17" org-junit-jupiter = "5.10.2" org-assertj-core = "3.25.3" net-thebugmc-gradle-sonatype-central-portal-publisher = "1.2.2" +me-champeau-jmh = "0.7.3" [libraries] org-bouncycastle-bcprov-jdk18on = { group = "org.bouncycastle", name = "bcprov-jdk18on", version.ref = "org-bouncycastle-bcprov-jdk18on" } @@ -12,4 +13,5 @@ org-junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", versi org-assertj-core = { group = "org.assertj", name = "assertj-core", version.ref = "org-assertj-core" } [plugins] -net-thebugmc-gradle-sonatype-central-portal-publisher = { id = "net.thebugmc.gradle.sonatype-central-portal-publisher", version.ref = "net-thebugmc-gradle-sonatype-central-portal-publisher" } \ No newline at end of file +net-thebugmc-gradle-sonatype-central-portal-publisher = { id = "net.thebugmc.gradle.sonatype-central-portal-publisher", version.ref = "net-thebugmc-gradle-sonatype-central-portal-publisher" } +me-champeau-jmh = { id = "me.champeau.jmh", version.ref = "me-champeau-jmh" } \ No newline at end of file diff --git a/src/jmh/java/io/github/ppzxc/crypto/CryptoDecryptBenchmark.java b/src/jmh/java/io/github/ppzxc/crypto/CryptoDecryptBenchmark.java new file mode 100644 index 0000000..412176a --- /dev/null +++ b/src/jmh/java/io/github/ppzxc/crypto/CryptoDecryptBenchmark.java @@ -0,0 +1,49 @@ +package io.github.ppzxc.crypto; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +public class CryptoDecryptBenchmark { + + @Param({"64", "128", "256", "512", "1024", "2048", "4096"}) + private int payloadSize; + + private Crypto aesCrypto; + private byte[] encryptedPayload; + + @Setup(Level.Trial) + public void setUp() throws CryptoException { + CryptoProvider.BOUNCY_CASTLE.addProvider(); + byte[] key = new byte[32]; // AES-256 + byte[] iv = "1234567890123456".getBytes(StandardCharsets.UTF_8); + aesCrypto = CryptoFactory.aes(key, Transformation.AES_CBC_PKCS7PADDING, CryptoProvider.BOUNCY_CASTLE, iv); + + byte[] plainPayload = new byte[payloadSize]; + ThreadLocalRandom.current().nextBytes(plainPayload); + encryptedPayload = aesCrypto.encrypt(plainPayload); + } + + @Benchmark + public byte[] decrypt() throws CryptoException { + return aesCrypto.decrypt(encryptedPayload); + } +} diff --git a/src/jmh/java/io/github/ppzxc/crypto/CryptoEncryptBenchmark.java b/src/jmh/java/io/github/ppzxc/crypto/CryptoEncryptBenchmark.java new file mode 100644 index 0000000..322a5ab --- /dev/null +++ b/src/jmh/java/io/github/ppzxc/crypto/CryptoEncryptBenchmark.java @@ -0,0 +1,47 @@ +package io.github.ppzxc.crypto; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(1) +public class CryptoEncryptBenchmark { + + @Param({"64", "128", "256", "512", "1024", "2048", "4096"}) + private int payloadSize; + + private Crypto aesCrypto; + private byte[] payload; + + @Setup(Level.Trial) + public void setUp() { + CryptoProvider.BOUNCY_CASTLE.addProvider(); + byte[] key = new byte[32]; // AES-256 + byte[] iv = "1234567890123456".getBytes(StandardCharsets.UTF_8); + aesCrypto = CryptoFactory.aes(key, Transformation.AES_CBC_PKCS7PADDING, CryptoProvider.BOUNCY_CASTLE, iv); + payload = new byte[payloadSize]; + ThreadLocalRandom.current().nextBytes(payload); + } + + @Benchmark + public byte[] encrypt() throws CryptoException { + return aesCrypto.encrypt(payload); + } +} diff --git a/src/test/java/io/github/ppzxc/crypto/AesCryptoTest.java b/src/test/java/io/github/ppzxc/crypto/AesCryptoTest.java index cd6ed8a..8331075 100644 --- a/src/test/java/io/github/ppzxc/crypto/AesCryptoTest.java +++ b/src/test/java/io/github/ppzxc/crypto/AesCryptoTest.java @@ -1,9 +1,13 @@ package io.github.ppzxc.crypto; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.github.ppzxc.crypto.AesCrypto.AesCryptoBuilder; import java.nio.charset.StandardCharsets; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -51,4 +55,19 @@ void should_throw_exception_5() { void should_throw_exception_6() { assertThatCode(() -> builder.charset(StandardCharsets.UTF_8)).doesNotThrowAnyException(); } + + @Test + void should_throw_exception_when_invalid_transformation_is_given() { + // given + AesCrypto crypto = AesCrypto.builder() + .secretKeySpec(new SecretKeySpec(new byte[16], "AES")) + .ivParameterSpec(new IvParameterSpec(new byte[16])) + .transformation(Transformation.RSA_ECB_PKCS1PADDING) // Wrong algorithm for AES + .cryptoProvider(CryptoProvider.BOUNCY_CASTLE) + .build(); + + // when & then + assertThatThrownBy(() -> crypto.encrypt(new byte[16])).isInstanceOf(CryptoException.class); + assertThatThrownBy(() -> crypto.decrypt(new byte[16])).isInstanceOf(CryptoException.class); + } } \ No newline at end of file diff --git a/src/test/java/io/github/ppzxc/crypto/AsymmetricKeyFactoryTest.java b/src/test/java/io/github/ppzxc/crypto/AsymmetricKeyFactoryTest.java index ec4f601..593f3e3 100644 --- a/src/test/java/io/github/ppzxc/crypto/AsymmetricKeyFactoryTest.java +++ b/src/test/java/io/github/ppzxc/crypto/AsymmetricKeyFactoryTest.java @@ -2,10 +2,16 @@ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -87,22 +93,40 @@ void should_created_1024_bit_key() assertThat(actual.getPrivate().getEncoded()).isEqualTo(expected.getPrivate().getEncoded()); } + @Test - void should_created_2048_bit_key() - throws NoSuchAlgorithmException, IOException, NoSuchProviderException, CryptoException { + void should_throw_exception_when_instantiate_private_constructor() + throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { // given - KeyPair expected = AsymmetricKeyFactory.generateRsa(2048); - AsymmetricKey given = AsymmetricKeyFactory.toAsymmetricKey(AsymmetricKey.Type.RSA, expected); + Constructor constructor = AsymmetricKeyFactory.class.getDeclaredConstructor(); + constructor.setAccessible(true); + + // when & then + assertThat(constructor.newInstance()).isNotNull(); + } + + @Test + void should_throw_exception_when_invalid_key_is_given() { + // given + String invalidKey = "INVALID"; + + // when & then + assertThatThrownBy(() -> AsymmetricKeyFactory.toPublicKey(AsymmetricKey.Type.RSA, invalidKey, X509EncodedKeySpec::new)) + .isNotNull(); + assertThatThrownBy(() -> AsymmetricKeyFactory.toPrivateKey(AsymmetricKey.Type.RSA, invalidKey, PKCS8EncodedKeySpec::new)) + .isNotNull(); + } + + @Test + void should_return_asymmetric_key_when_type_is_given() throws Exception { + // given + AsymmetricKey.Type type = AsymmetricKey.Type.RSA; // when - KeyPair actual = AsymmetricKeyFactory.generate(given); + AsymmetricKey actual = AsymmetricKeyFactory.generate(type); // then - assertThat(actual.getPublic().getAlgorithm()).isEqualTo(expected.getPublic().getAlgorithm()); - assertThat(actual.getPublic().getFormat()).isEqualTo(expected.getPublic().getFormat()); - assertThat(actual.getPublic().getEncoded()).isEqualTo(expected.getPublic().getEncoded()); - assertThat(actual.getPrivate().getAlgorithm()).isEqualTo(expected.getPrivate().getAlgorithm()); - assertThat(actual.getPrivate().getFormat()).isEqualTo(expected.getPrivate().getFormat()); - assertThat(actual.getPrivate().getEncoded()).isEqualTo(expected.getPrivate().getEncoded()); + assertThat(actual).isNotNull(); + assertThat(actual.getType()).isEqualTo(type); } } \ No newline at end of file diff --git a/src/test/java/io/github/ppzxc/crypto/CryptoFactoryTest.java b/src/test/java/io/github/ppzxc/crypto/CryptoFactoryTest.java index a2c5540..5a0c3ad 100644 --- a/src/test/java/io/github/ppzxc/crypto/CryptoFactoryTest.java +++ b/src/test/java/io/github/ppzxc/crypto/CryptoFactoryTest.java @@ -1,10 +1,14 @@ package io.github.ppzxc.crypto; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import io.github.ppzxc.fixh.ByteArrayUtils; import io.github.ppzxc.fixh.StringUtils; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; +import java.security.KeyPair; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -60,4 +64,34 @@ void should_return_crypto_3() { void should_return_crypto_7() { assertThatCode(() -> CryptoFactory.rsa(AsymmetricKeyFactory.generateRsa())).doesNotThrowAnyException(); } + + @Test + void should_return_crypto_when_ecb_mode_is_given() { + // given + byte[] key = ByteArrayUtils.giveMeOne(16); + Transformation transformation = Transformation.AES_ECB_PKCS5PADDING; + + // when & then + assertThatCode(() -> CryptoFactory.aes(key, transformation, null)).doesNotThrowAnyException(); + } + + @Test + void should_return_crypto_when_only_public_key_is_given() throws Exception { + // given + KeyPair keyPair = AsymmetricKeyFactory.generateRsa(); + + // when & then + assertThatCode(() -> CryptoFactory.rsa(keyPair.getPublic())).doesNotThrowAnyException(); + } + + @Test + void should_throw_exception_when_instantiate_private_constructor() + throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + // given + Constructor constructor = CryptoFactory.class.getDeclaredConstructor(); + constructor.setAccessible(true); + + // when & then + assertThat(constructor.newInstance()).isNotNull(); + } } \ No newline at end of file diff --git a/src/test/java/io/github/ppzxc/crypto/CryptoSecureRandomTest.java b/src/test/java/io/github/ppzxc/crypto/CryptoSecureRandomTest.java new file mode 100644 index 0000000..b4ebe30 --- /dev/null +++ b/src/test/java/io/github/ppzxc/crypto/CryptoSecureRandomTest.java @@ -0,0 +1,106 @@ +package io.github.ppzxc.crypto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.lang.reflect.Constructor; +import java.security.SecureRandom; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class CryptoSecureRandomTest { + + @BeforeAll + static void beforeAll() { + CryptoProvider.BOUNCY_CASTLE.addProvider(); + } + + @Test + void should_return_secure_random_when_default_called() { + // given + + // when + SecureRandom actual = CryptoSecureRandom.getSecureRandom(); + + // then + assertThat(actual).isNotNull(); + assertThat(actual.getAlgorithm()).isEqualTo("SHA1PRNG"); + } + + @Test + void should_return_secure_random_when_algorithm_is_given() { + // given + String algorithm = "SHA1PRNG"; + + // when + SecureRandom actual = CryptoSecureRandom.getSecureRandom(algorithm); + + // then + assertThat(actual).isNotNull(); + assertThat(actual.getAlgorithm()).isEqualTo(algorithm); + } + + @Test + void should_return_secure_random_when_algorithm_and_provider_string_are_given() { + // given + String algorithm = "SHA1PRNG"; + String provider = "SUN"; + + // when + SecureRandom actual = CryptoSecureRandom.getSecureRandom(algorithm, provider); + + // then + assertThat(actual).isNotNull(); + assertThat(actual.getAlgorithm()).isEqualTo(algorithm); + assertThat(actual.getProvider().getName()).isEqualTo(provider); + } + + @Test + void should_return_secure_random_when_algorithm_and_crypto_provider_are_given() { + // given + String algorithm = "NONCEANDIV"; + CryptoProvider provider = CryptoProvider.BOUNCY_CASTLE; + + // when + SecureRandom actual = CryptoSecureRandom.getSecureRandom(algorithm, provider); + + // then + assertThat(actual).isNotNull(); + assertThat(actual.getAlgorithm()).isEqualTo(algorithm); + assertThat(actual.getProvider().getName()).isEqualTo(provider.getCode()); + } + + @Test + void should_throw_exception_when_invalid_algorithm_is_given() { + // given + String invalidAlgorithm = "INVALID_ALGORITHM"; + + // when & then + assertThatThrownBy(() -> CryptoSecureRandom.getSecureRandom(invalidAlgorithm)) + .isInstanceOf(CryptoRuntimeException.class); + } + + @Test + void should_throw_exception_when_invalid_provider_is_given() { + // given + String algorithm = "SHA1PRNG"; + String invalidProvider = "INVALID_PROVIDER"; + + // when & then + assertThatThrownBy(() -> CryptoSecureRandom.getSecureRandom(algorithm, invalidProvider)) + .isInstanceOf(CryptoRuntimeException.class); + } + + @Test + void should_be_able_to_instantiate_private_constructor_via_reflection() throws Exception { + // given + Constructor constructor = CryptoSecureRandom.class.getDeclaredConstructor(); + constructor.setAccessible(true); + + // when + CryptoSecureRandom instance = constructor.newInstance(); + + // then + assertThat(instance).isNotNull(); + } +} diff --git a/src/test/java/io/github/ppzxc/crypto/EmptyCryptoTest.java b/src/test/java/io/github/ppzxc/crypto/EmptyCryptoTest.java index 3626427..05d4baa 100644 --- a/src/test/java/io/github/ppzxc/crypto/EmptyCryptoTest.java +++ b/src/test/java/io/github/ppzxc/crypto/EmptyCryptoTest.java @@ -85,4 +85,28 @@ void should_return_empty_when_string_decrypt() throws CryptoException { // then assertThat(actual).isEmpty(); } + + @Test + void should_return_null_when_byte_decrypt_to_string() throws CryptoException { + // given + Crypto crypto = EmptyCrypto.create(); + + // when + String actual = crypto.decryptToString(ByteArrayUtils.giveMeOne()); + + // then + assertThat(actual).isNull(); + } + + @Test + void should_return_null_when_string_decrypt_to_string() throws CryptoException { + // given + Crypto crypto = EmptyCrypto.create(); + + // when + String actual = crypto.decryptToString(StringUtils.giveMeOne()); + + // then + assertThat(actual).isNull(); + } } \ No newline at end of file diff --git a/src/test/java/io/github/ppzxc/crypto/ExceptionTest.java b/src/test/java/io/github/ppzxc/crypto/ExceptionTest.java new file mode 100644 index 0000000..a87c371 --- /dev/null +++ b/src/test/java/io/github/ppzxc/crypto/ExceptionTest.java @@ -0,0 +1,76 @@ +package io.github.ppzxc.crypto; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ExceptionTest { + + @Test + void should_create_CryptoException_with_various_constructors() { + // given + Exception cause = new RuntimeException("cause"); + String message = "message"; + + // when + CryptoException ex1 = new CryptoException(); + CryptoException ex2 = new CryptoException(message); + CryptoException ex3 = new CryptoException(message, cause); + CryptoException ex4 = new CryptoException(cause); + + // then + assertThat(ex1).isNotNull(); + assertThat(ex2.getMessage()).isEqualTo(message); + assertThat(ex3.getMessage()).isEqualTo(message); + assertThat(ex3.getCause()).isEqualTo(cause); + assertThat(ex4.getCause()).isEqualTo(cause); + } + + @Test + void should_create_CryptoRuntimeException_with_various_constructors() { + // given + Exception cause = new RuntimeException("cause"); + String message = "message"; + + // when + CryptoRuntimeException ex1 = new CryptoRuntimeException(); + CryptoRuntimeException ex2 = new CryptoRuntimeException(message); + CryptoRuntimeException ex3 = new CryptoRuntimeException(message, cause); + CryptoRuntimeException ex4 = new CryptoRuntimeException(cause); + CryptoRuntimeException ex5 = new CryptoRuntimeException(message, cause, true, true); + CryptoRuntimeException ex6 = CryptoRuntimeException.notSupportedDecrypt(); + + // then + assertThat(ex1).isNotNull(); + assertThat(ex2.getMessage()).isEqualTo(message); + assertThat(ex3.getMessage()).isEqualTo(message); + assertThat(ex3.getCause()).isEqualTo(cause); + assertThat(ex4.getCause()).isEqualTo(cause); + assertThat(ex5.getMessage()).isEqualTo(message); + assertThat(ex5.getCause()).isEqualTo(cause); + assertThat(ex6.getMessage()).isEqualTo("not supported decrypt"); + } + + @Test + void should_create_CryptoException_not_supported_decrypt() { + // given & when + CryptoException ex = CryptoException.notSupportedDecrypt(); + + // then + assertThat(ex.getMessage()).isEqualTo("not supported decrypt"); + } + + @Test + void should_create_CryptoException_with_all_params() { + // given + Exception cause = new RuntimeException("cause"); + String message = "message"; + + // when + CryptoException ex = new CryptoException(message, cause, true, true); + + // then + assertThat(ex.getMessage()).isEqualTo(message); + assertThat(ex.getCause()).isEqualTo(cause); + } +} diff --git a/src/test/java/io/github/ppzxc/crypto/SymmetricKeyTest.java b/src/test/java/io/github/ppzxc/crypto/SymmetricKeyTest.java index 820c260..74605be 100644 --- a/src/test/java/io/github/ppzxc/crypto/SymmetricKeyTest.java +++ b/src/test/java/io/github/ppzxc/crypto/SymmetricKeyTest.java @@ -59,4 +59,16 @@ void should_return_key_when_input_key_bytes() { // then assertThat(actual.getKeyByteArray()).isEqualTo(expected.getBytes(StandardCharsets.UTF_8)); } + + @Test + void should_return_key_with_custom_charset() { + // given + String expected = StringUtils.giveMeOne(); + + // when + SymmetricKey actual = new SymmetricKey(expected); + + // then + assertThat(actual.getKeyByteArray(StandardCharsets.UTF_16)).isEqualTo(expected.getBytes(StandardCharsets.UTF_16)); + } } \ No newline at end of file diff --git a/src/test/java/io/github/ppzxc/crypto/TransformationEnumTest.java b/src/test/java/io/github/ppzxc/crypto/TransformationEnumTest.java new file mode 100644 index 0000000..6f6b57e --- /dev/null +++ b/src/test/java/io/github/ppzxc/crypto/TransformationEnumTest.java @@ -0,0 +1,81 @@ +package io.github.ppzxc.crypto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class TransformationEnumTest { + + @Test + void should_return_TransformationType_when_valid_code_is_given() { + // given + String code = "AES"; + + // when + TransformationType actual = TransformationType.of(code); + + // then + assertThat(actual).isEqualTo(TransformationType.ADVANCED_ENCRYPTION_STANDARD); + assertThat(actual.getCode()).isEqualTo(code); + } + + @Test + void should_throw_exception_when_invalid_TransformationType_code_is_given() { + // given + String invalidCode = "INVALID"; + + // when & then + assertThatThrownBy(() -> TransformationType.of(invalidCode)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("not supported 'AlgorithmType'"); + } + + @Test + void should_return_TransformationMode_when_valid_code_is_given() { + // given + String code = "CBC"; + + // when + TransformationMode actual = TransformationMode.of(code); + + // then + assertThat(actual).isEqualTo(TransformationMode.CIPHER_BLOCK_CHAINING); + assertThat(actual.getCode()).isEqualTo(code); + } + + @Test + void should_throw_exception_when_invalid_TransformationMode_code_is_given() { + // given + String invalidCode = "INVALID"; + + // when & then + assertThatThrownBy(() -> TransformationMode.of(invalidCode)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("not supported 'AlgorithmMode'"); + } + + @Test + void should_return_TransformationPkcs_when_valid_code_is_given() { + // given + String code = "PKCS7Padding"; + + // when + TransformationPkcs actual = TransformationPkcs.of(code); + + // then + assertThat(actual).isEqualTo(TransformationPkcs.PKCS7PADDING); + assertThat(actual.getCode()).isEqualTo(code); + } + + @Test + void should_throw_exception_when_invalid_TransformationPkcs_code_is_given() { + // given + String invalidCode = "INVALID"; + + // when & then + assertThatThrownBy(() -> TransformationPkcs.of(invalidCode)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("not supported 'AlgorithmPkcs'"); + } +} diff --git a/src/test/java/io/github/ppzxc/crypto/TransformationTest.java b/src/test/java/io/github/ppzxc/crypto/TransformationTest.java index c46d3e8..9fe5395 100644 --- a/src/test/java/io/github/ppzxc/crypto/TransformationTest.java +++ b/src/test/java/io/github/ppzxc/crypto/TransformationTest.java @@ -75,6 +75,14 @@ void should_return_transformation_pkcs_mode() { .isEqualTo(TransformationPkcs.PKCS7PADDING); } + @Test + void should_throw_exception_when_unsupported_combination() { + // RSA with PKCS7Padding is not in the enum + assertThatCode(() -> Transformation.of("RSA", "CBC", "PKCS7Padding")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("not supported transformation"); + } + private static String[] nullEmptyBlank() { return new String[]{null, "", " "}; }