Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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"
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 3 additions & 1 deletion libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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" }
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" }
49 changes: 49 additions & 0 deletions src/jmh/java/io/github/ppzxc/crypto/CryptoDecryptBenchmark.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
47 changes: 47 additions & 0 deletions src/jmh/java/io/github/ppzxc/crypto/CryptoEncryptBenchmark.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
19 changes: 19 additions & 0 deletions src/test/java/io/github/ppzxc/crypto/AesCryptoTest.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
}
46 changes: 35 additions & 11 deletions src/test/java/io/github/ppzxc/crypto/AsymmetricKeyFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<AsymmetricKeyFactory> 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);
}
}
34 changes: 34 additions & 0 deletions src/test/java/io/github/ppzxc/crypto/CryptoFactoryTest.java
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<CryptoFactory> constructor = CryptoFactory.class.getDeclaredConstructor();
constructor.setAccessible(true);

// when & then
assertThat(constructor.newInstance()).isNotNull();
}
}
Loading