From c9a54d56fbcc95f896bcd284724c968fd1a0ca08 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 21 Aug 2024 10:57:47 +0300 Subject: [PATCH] [#6] cover the cryptography module with junit tests Signed-off-by: Ori Bruk --- .../java/info/frostfs/sdk/ArrayHelper.java | 8 + .../main/java/info/frostfs/sdk/Base58.java | 8 +- .../main/java/info/frostfs/sdk/Helper.java | 14 +- .../java/info/frostfs/sdk/KeyExtension.java | 55 ++++--- .../info/frostfs/sdk/ArrayHelperTest.java | 66 ++++++++ .../java/info/frostfs/sdk/Base58Test.java | 54 +++++++ .../java/info/frostfs/sdk/HelperTest.java | 110 +++++++++++++ .../info/frostfs/sdk/KeyExtensionTest.java | 151 ++++++++++++++++++ 8 files changed, 432 insertions(+), 34 deletions(-) create mode 100644 cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java create mode 100644 cryptography/src/test/java/info/frostfs/sdk/Base58Test.java create mode 100644 cryptography/src/test/java/info/frostfs/sdk/HelperTest.java create mode 100644 cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java diff --git a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java index 915f202..b21a3f2 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java @@ -1,10 +1,18 @@ package info.frostfs.sdk; +import static java.util.Objects.isNull; + public class ArrayHelper { + private static final String ERROR_MESSAGE = "One of the input parameters is null"; + private ArrayHelper() { } public static byte[] concat(byte[] startArray, byte[] endArray) { + if (isNull(startArray) || isNull(endArray)) { + throw new IllegalArgumentException(ERROR_MESSAGE); + } + byte[] result = new byte[startArray.length + endArray.length]; System.arraycopy(startArray, 0, result, 0, startArray.length); diff --git a/cryptography/src/main/java/info/frostfs/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java index 860299d..4067687 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Base58.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Base58.java @@ -16,6 +16,8 @@ public class Base58 { private static final char ENCODED_ZERO = ALPHABET[0]; private static final char BASE58_ASCII_MAX_VALUE = 128; private static final int[] INDEXES = new int[BASE58_ASCII_MAX_VALUE]; + private static final String ERROR_VALUE_MISSING_MESSAGE = "Input value is missing"; + private static final String ERROR_INVALID_CHARACTER_TEMPLATE = "Invalid character in Base58: 0x%04x"; static { Arrays.fill(INDEXES, -1); @@ -29,7 +31,7 @@ public class Base58 { public static byte[] base58CheckDecode(String input) { if (StringUtils.isEmpty(input)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } byte[] buffer = decode(input); @@ -50,7 +52,7 @@ public class Base58 { public static String base58CheckEncode(byte[] data) { if (isNull(data)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } byte[] checksum = getSha256(getSha256(data)); @@ -99,7 +101,7 @@ public class Base58 { char c = input.charAt(i); int digit = c < BASE58_ASCII_MAX_VALUE ? INDEXES[c] : -1; if (digit < 0) { - throw new IllegalArgumentException(String.format("Invalid character in Base58: 0x%04x", (int) c)); + throw new IllegalArgumentException(String.format(ERROR_INVALID_CHARACTER_TEMPLATE, (int) c)); } input58[i] = (byte) digit; } diff --git a/cryptography/src/main/java/info/frostfs/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java index a864301..529da35 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -11,6 +11,8 @@ import java.security.NoSuchAlgorithmException; import static java.util.Objects.isNull; public class Helper { + private static final String ERROR_MESSAGE = "Input value is missing"; + private static final String SHA256 = "SHA-256"; private static final int RIPEMD_160_HASH_BYTE_LENGTH = 20; private Helper() { @@ -18,7 +20,7 @@ public class Helper { public static byte[] getRipemd160(byte[] value) { if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_MESSAGE); } var hash = new byte[RIPEMD_160_HASH_BYTE_LENGTH]; @@ -30,7 +32,7 @@ public class Helper { public static MessageDigest getSha256Instance() { try { - return MessageDigest.getInstance("SHA-256"); + return MessageDigest.getInstance(SHA256); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } @@ -38,7 +40,7 @@ public class Helper { public static byte[] getSha256(byte[] value) { if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_MESSAGE); } return getSha256Instance().digest(value); @@ -46,15 +48,15 @@ public class Helper { public static ByteString getSha256(Message value) { if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_MESSAGE); } return ByteString.copyFrom(getSha256(value.toByteArray())); } public static String getHexString(byte[] value) { - if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + if (isNull(value) || value.length == 0) { + throw new IllegalArgumentException(ERROR_MESSAGE); } return String.format("%0" + (value.length << 1) + "x", new BigInteger(1, value)); diff --git a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java index f3cf7de..25ce3af 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java @@ -31,6 +31,8 @@ import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class KeyExtension { public static final byte NEO_ADDRESS_VERSION = 0x35; + private static final String CURVE_NAME = "secp256r1"; + private static final String SECURITY_ALGORITHM = "EC"; private static final int PS_IN_HASH160 = 0x0C; private static final int DECODE_ADDRESS_LENGTH = 21; private static final int COMPRESSED_PUBLIC_KEY_LENGTH = 33; @@ -38,6 +40,13 @@ public class KeyExtension { private static final int CHECK_SIG_DESCRIPTOR = ByteBuffer .wrap(getSha256("System.Crypto.CheckSig".getBytes(StandardCharsets.US_ASCII))) .order(ByteOrder.LITTLE_ENDIAN).getInt(); + private static final String ERROR_VALUE_MISSING_MESSAGE = "Input value is missing"; + private static final String ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE = + "PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s"; + private static final String ERROR_UNCOMPRESSED_PUBLIC_KEY_TEMPLATE = + "Compress argument isn't uncompressed public key. Expected length=%s, actual=%s"; + private static final String ERROR_COMPRESSED_PUBLIC_KEY_TEMPLATE = + "Decompress argument isn't compressed public key. Expected length=%s, actual=%s"; private KeyExtension() { } @@ -45,10 +54,9 @@ public class KeyExtension { public static byte[] compress(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != UNCOMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format("Compress argument isn't uncompressed public key. Expected length=%s, actual=%s", - UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) - ); + throw new IllegalArgumentException(String.format( + ERROR_UNCOMPRESSED_PUBLIC_KEY_TEMPLATE, UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + )); } var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); @@ -58,7 +66,7 @@ public class KeyExtension { public static byte[] getPrivateKeyFromWIF(String wif) { if (StringUtils.isEmpty(wif)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } var data = Base58.base58CheckDecode(wif); @@ -87,11 +95,11 @@ public class KeyExtension { ECPrivateKeyParameters ecParams = new ECPrivateKeyParameters(fromUnsignedByteArray(privateKey), domain); ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( - "secp256r1", params.getCurve(), params.getG(), params.getN(), params.getH() + CURVE_NAME, params.getCurve(), params.getG(), params.getN(), params.getH() ); ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(ecParams.getD(), ecParameterSpec); try { - KeyFactory kf = KeyFactory.getInstance("EC"); + KeyFactory kf = KeyFactory.getInstance(SECURITY_ALGORITHM); return kf.generatePrivate(privateKeySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new RuntimeException(e); @@ -103,8 +111,7 @@ public class KeyExtension { if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { throw new IllegalArgumentException( - String.format("Decompress argument isn't compressed public key. Expected length=%s, actual=%s", - COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) + String.format(ERROR_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) ); } @@ -118,13 +125,13 @@ public class KeyExtension { publicParams.getQ().getRawXCoord().toBigInteger(), publicParams.getQ().getRawYCoord().toBigInteger() ); ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( - "secp256r1", secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), - secp256R1.getH(), secp256R1.getSeed() + CURVE_NAME, secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), secp256R1.getH(), + secp256R1.getSeed() ); ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(point, ecParameterSpec); try { - KeyFactory kf = KeyFactory.getInstance("EC"); + KeyFactory kf = KeyFactory.getInstance(SECURITY_ALGORITHM); return kf.generatePublic(publicKeySpec); } catch (Exception e) { throw new RuntimeException(e); @@ -141,19 +148,18 @@ public class KeyExtension { public static String publicKeyToAddress(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", - COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) - ); + throw new IllegalArgumentException(String.format( + ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + )); } - return toAddress(getScriptHash(publicKey), NEO_ADDRESS_VERSION); + return toAddress(getScriptHash(publicKey)); } - private static String toAddress(byte[] scriptHash, byte version) { + private static String toAddress(byte[] scriptHash) { checkInputValue(scriptHash); byte[] data = new byte[DECODE_ADDRESS_LENGTH]; - data[0] = version; + data[0] = NEO_ADDRESS_VERSION; System.arraycopy(scriptHash, 0, data, 1, scriptHash.length); return Base58.base58CheckEncode(data); } @@ -171,10 +177,9 @@ public class KeyExtension { private static byte[] createSignatureRedeemScript(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", - COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) - ); + throw new IllegalArgumentException(String.format( + ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + )); } var script = new byte[]{PS_IN_HASH160, COMPRESSED_PUBLIC_KEY_LENGTH}; //PUSHDATA1 33 @@ -186,8 +191,8 @@ public class KeyExtension { } private static void checkInputValue(byte[] data) { - if (isNull(data)) { - throw new IllegalArgumentException("Input value is missing"); + if (isNull(data) || data.length == 0) { + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } } } diff --git a/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java new file mode 100644 index 0000000..ab8a1e1 --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java @@ -0,0 +1,66 @@ +package info.frostfs.sdk; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ArrayHelperTest { + + @Test + void concatTest_success() { + //Given + var startBytes = new byte[]{1, 2, 3, 4, 5}; + var endBytes = new byte[]{6, 7, 8, 9}; + + //When + var result = ArrayHelper.concat(startBytes, endBytes); + + //Then + assertThat(startBytes).hasSize(5); + assertThat(endBytes).hasSize(4); + assertThat(result).hasSize(9).startsWith(startBytes).endsWith(endBytes); + } + + @Test + void concatTest_endArrayIsEmpty() { + //Given + var startBytes = new byte[]{1, 2, 3, 4, 5}; + var endBytes = new byte[]{}; + + //When + var result = ArrayHelper.concat(startBytes, endBytes); + + //Then + assertThat(startBytes).hasSize(5); + assertThat(endBytes).hasSize(0); + assertThat(result).hasSize(5).startsWith(startBytes); + } + + @Test + void concatTest_startArrayIsEmpty() { + //Given + var startBytes = new byte[]{}; + var endBytes = new byte[]{6, 7, 8, 9}; + + //When + var result = ArrayHelper.concat(startBytes, endBytes); + + //Then + assertThat(startBytes).hasSize(0); + assertThat(endBytes).hasSize(4); + assertThat(result).hasSize(4).startsWith(endBytes); + } + + @Test + void concatTest_givenParamsIsNull() { + //Given + var startBytes = new byte[]{1, 2, 3, 4, 5}; + var endBytes = new byte[]{6, 7, 8, 9}; + + //When + Then + assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(startBytes, null)); + assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(null, endBytes)); + assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(null, null)); + } +} diff --git a/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java b/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java new file mode 100644 index 0000000..7ac3d9d --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java @@ -0,0 +1,54 @@ +package info.frostfs.sdk; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class Base58Test { + private static final String WIF = "L1YS4myg3xHPvi3FHeLaEt7G8upwJaWL5YLV7huviuUtXFpzBMqZ"; + private static final byte[] DECODE = new byte[]{ + -128, -128, -5, 30, -36, -118, 85, -67, -6, 81, 43, 93, -38, 106, 21, -88, 127, 15, 125, -79, -17, -40, 77, + -15, 122, -88, 72, 109, -47, 125, -80, -40, -38, 1 + }; + + @Test + void base58DecodeEncode() { + //When + Then + assertEquals(WIF, Base58.base58CheckEncode(Base58.base58CheckDecode(WIF))); + } + + @Test + void base58Decode_success() { + //When + var decode = Base58.base58CheckDecode(WIF); + + //Then + assertThat(decode).hasSize(34).containsExactly(DECODE); + } + + @Test + void base58Decode_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode(null)); + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("")); + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("WIF")); + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("fh")); + } + + @Test + void base58Encode_success() { + //When + var encode = Base58.base58CheckEncode(DECODE); + + //Then + assertEquals(WIF, encode); + } + + @Test + void base58Encode_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckEncode(null)); + } +} diff --git a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java new file mode 100644 index 0000000..67cfaba --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java @@ -0,0 +1,110 @@ +package info.frostfs.sdk; + +import com.google.protobuf.Message; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class HelperTest { + + @Test + void getRipemd160_success() { + //Given + var value = new byte[]{1, 2, 3, 4, 5}; + var expected = new byte[] + {-21, -126, 92, 75, 36, -12, 37, 7, 122, 6, 124, -61, -66, -12, 87, 120, 63, 90, -41, 5}; + //When + var result = Helper.getRipemd160(value); + + //Then + assertThat(result).hasSize(20).containsExactly(expected); + } + + @Test + void getRipemd160_givenParamIsNull() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Helper.getRipemd160(null)); + } + + @Test + void getRipemd160_givenParamsIsEmpty() { + //Given + var value = new byte[]{}; + var expected = new byte[] + {-100, 17, -123, -91, -59, -23, -4, 84, 97, 40, 8, -105, 126, -24, -11, 72, -78, 37, -115, 49}; + //When + var result = Helper.getRipemd160(value); + + //Then + assertThat(result).hasSize(20).containsExactly(expected); + } + + @Test + void getSha256Instance() { + //When + var result = Helper.getSha256Instance(); + + //Then + assertEquals("SHA-256", result.getAlgorithm()); + } + + @Test + void getSha256_bytes_success() { + //Given + var value = new byte[]{1, 2, 3, 4, 5}; + var expected = new byte[]{ + 116, -8, 31, -31, 103, -39, -101, 76, -76, 29, 109, 12, -51, -88, 34, 120, -54, -18, -97, 62, 47, 37, + -43, -27, -93, -109, 111, -13, -36, -20, 96, -48 + }; + + //When + var result = Helper.getSha256(value); + + //Then + assertThat(result).hasSize(32).containsExactly(expected); + } + + @Test + void getSha256_bytes_givenParamIsNull() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Helper.getSha256((byte[]) null)); + assertThrows(IllegalArgumentException.class, () -> Helper.getSha256((Message) null)); + } + + @Test + void getSha256_bytes_givenParamsIsEmpty() { + //Given + var value = new byte[]{}; + var expected = new byte[]{ + -29, -80, -60, 66, -104, -4, 28, 20, -102, -5, -12, -56, -103, 111, -71, 36, 39, -82, 65, -28, 100, + -101, -109, 76, -92, -107, -103, 27, 120, 82, -72, 85 + }; + + //When + var result = Helper.getSha256(value); + + //Then + assertThat(result).hasSize(32).containsExactly(expected); + } + + @Test + void getHexString_success() { + //Given + var value = new byte[]{1, 2, 3, 4, 5}; + + //When + var result = Helper.getHexString(value); + + //Then + assertEquals("0102030405", result); + } + + @Test + void getHexString_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Helper.getHexString(null)); + assertThrows(IllegalArgumentException.class, () -> Helper.getHexString(new byte[]{})); + } +} diff --git a/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java new file mode 100644 index 0000000..c54025a --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java @@ -0,0 +1,151 @@ +package info.frostfs.sdk; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class KeyExtensionTest { + private static final String WIF = "L1YS4myg3xHPvi3FHeLaEt7G8upwJaWL5YLV7huviuUtXFpzBMqZ"; + private static final String OWNER_ID = "NVxUSpEEJzYXZZtUs18PrJTD9QZkLLNQ8S"; + private static final byte[] PRIVATE_KEY = new byte[]{ + -128, -5, 30, -36, -118, 85, -67, -6, 81, 43, 93, -38, 106, 21, -88, 127, 15, 125, -79, -17, -40, 77, -15, + 122, -88, 72, 109, -47, 125, -80, -40, -38 + }; + private static final byte[] PUBLIC_KEY = new byte[]{ + 3, 80, -111, 65, 23, 36, -4, -69, 80, 102, 98, -13, 13, -79, 24, 126, -116, 90, 56, 44, 127, 106, -125, 99, + -48, -94, 104, 81, 120, -110, 30, 80, 70 + }; + private static final byte[] UNCOMPRESSED_PUBLIC_KEY = new byte[]{ + 4, 80, -111, 65, 23, 36, -4, -69, 80, 102, 98, -13, 13, -79, 24, 126, -116, 90, 56, 44, 127, 106, -125, 99, + -48, -94, 104, 81, 120, -110, 30, 80, 70, 76, -18, 53, -9, 79, -8, -25, -69, 12, 89, -103, 15, 126, 118, + -68, -73, 65, -57, -26, 75, 4, -51, -40, -20, 75, 89, -59, 111, 96, -80, 56, 13 + }; + + @Test + void getPrivateKeyFromWIF_success() { + //When + var privateKey = KeyExtension.getPrivateKeyFromWIF(WIF); + + //Then + assertThat(privateKey).hasSize(32).containsExactly(PRIVATE_KEY); + } + + @Test + void getPrivateKeyFromWIF_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPrivateKeyFromWIF("")); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPrivateKeyFromWIF(null)); + } + + @Test + void loadPublicKey_success() { + //When + var publicKey = KeyExtension.loadPublicKey(PRIVATE_KEY); + + //Then + assertThat(publicKey).hasSize(33).containsExactly(PUBLIC_KEY); + } + + @Test + void loadPublicKey_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPublicKey(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPublicKey(new byte[]{})); + } + + @Test + void loadPrivateKey_success() { + //When + var privateKey = KeyExtension.loadPrivateKey(PRIVATE_KEY); + + //Then + assertThat(privateKey.getEncoded()).hasSize(67).endsWith(PRIVATE_KEY); + assertEquals("EC", privateKey.getAlgorithm()); + assertEquals("PKCS#8", privateKey.getFormat()); + } + + @Test + void loadPrivateKey_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPrivateKey(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPrivateKey(new byte[]{})); + } + + @Test + void getPublicKeyFromBytes_success() { + //When + var publicKey = KeyExtension.getPublicKeyFromBytes(PUBLIC_KEY); + + //Then + assertThat(publicKey.getEncoded()).hasSize(91).endsWith(UNCOMPRESSED_PUBLIC_KEY); + assertEquals("EC", publicKey.getAlgorithm()); + assertEquals("X.509", publicKey.getFormat()); + } + + @Test + void getPublicKeyFromBytes_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(PRIVATE_KEY)); + } + + @Test + void getScriptHash_success() { + //Given + var expected = new byte[]{ + 110, 42, -125, -76, -25, -44, -94, 22, -98, 117, -100, -5, 103, 74, -128, -51, 37, -116, -102, 71 + }; + + //When + var hash = KeyExtension.getScriptHash(PUBLIC_KEY); + + //Then + assertThat(hash).hasSize(20).containsExactly(expected); + } + + @Test + void getScriptHash_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(PRIVATE_KEY)); + } + + @Test + void publicKeyToAddress_success() { + //When + var address = KeyExtension.publicKeyToAddress(PUBLIC_KEY); + + //Then + assertEquals(OWNER_ID, address); + } + + @Test + void publicKeyToAddress_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(PRIVATE_KEY)); + } + + @Test + void compress_success() { + //When + var publicKey = KeyExtension.compress(UNCOMPRESSED_PUBLIC_KEY); + + //Then + assertThat(publicKey).hasSize(33).containsExactly(PUBLIC_KEY); + } + + @Test + void compress_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(PUBLIC_KEY)); + } + +}