[#6] cover the cryptography module with junit tests

Signed-off-by: Ori Bruk <o.bruk@yadro.com>
This commit is contained in:
Ori Bruk 2024-08-21 10:57:47 +03:00 committed by Bruk Ori
parent 0d76332f01
commit c9a54d56fb
8 changed files with 432 additions and 34 deletions

View file

@ -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);

View file

@ -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;
}

View file

@ -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));

View file

@ -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);
}
}
}