package qora.crypto; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import qora.account.Account; import qora.account.PrivateKeyAccount; import utils.Pair; import com.google.common.primitives.Bytes; public class Crypto { public static final byte ADDRESS_VERSION = 58; private static Crypto instance; public static Crypto getInstance() { if(instance == null) { instance = new Crypto(); } return instance; } private Crypto() { } public byte[] digest(byte[] input) { try { //SHA256 MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); return sha256.digest(input); } catch (NoSuchAlgorithmException e) { return null; } } public byte[] doubleDigest(byte[] input) { //DOUBLE SHA256 return this.digest(this.digest(input)); } public Pair<byte[], byte[]> createKeyPair(byte[] seed) { try { //GENERATE PUBLIC KEY return Ed25519.createKeyPair(seed); } catch (Exception e) { e.printStackTrace(); return null; } } public String getAddress(byte[] publicKey) { //SHA256 PUBLICKEY FOR PROTECTION byte[] publicKeyHash = this.digest(publicKey); //RIPEMD160 TO CREATE A SHORTER ADDRESS RIPEMD160 ripEmd160 = new RIPEMD160(); publicKeyHash = ripEmd160.digest(publicKeyHash); //CONVERT TO LIST List<Byte> addressList = new ArrayList<Byte>(); //ADD VERSION BYTE Byte versionByte = Byte.valueOf(ADDRESS_VERSION); addressList.add(versionByte); addressList.addAll(Bytes.asList(publicKeyHash)); //GENERATE CHECKSUM byte[] checkSum = this.doubleDigest(Bytes.toArray(addressList)); //ADD FIRST 4 BYTES OF CHECKSUM TO ADDRESS addressList.add(checkSum[0]); addressList.add(checkSum[1]); addressList.add(checkSum[2]); addressList.add(checkSum[3]); //BASE58 ENCODE ADDRESS String address = Base58.encode(Bytes.toArray(addressList)); return address; } public boolean isValidAddress(String address) { try { //BASE 58 DECODE byte[] addressBytes = Base58.decode(address); //CHECK BYTES if(addressBytes.length != Account.ADDRESS_LENGTH) { return false; } //CHECK VERSION if(addressBytes[0] != ADDRESS_VERSION) { return false; } //CONVERT TO LIST List<Byte> addressList = new ArrayList<Byte>(); addressList.addAll(Bytes.asList(addressBytes)); //REMOVE CHECKSUM byte[] checkSum = new byte[4]; checkSum[3] = addressList.remove(addressList.size() - 1); checkSum[2] = addressList.remove(addressList.size() - 1); checkSum[1] = addressList.remove(addressList.size() - 1); checkSum[0] = addressList.remove(addressList.size() - 1); //GENERATE ADDRESS CHECKSUM byte[] digest = this.doubleDigest(Bytes.toArray(addressList)); byte[] checkSumTwo = new byte[4]; checkSumTwo[0] = digest[0]; checkSumTwo[1] = digest[1]; checkSumTwo[2] = digest[2]; checkSumTwo[3] = digest[3]; //CHECK IF CHECKSUMS ARE THE SAME return Arrays.equals(checkSum, checkSumTwo); } catch(Exception e) { //ERROR DECODING return false; } } public byte[] sign(PrivateKeyAccount account, byte[] message) { try { //GET SIGNATURE return Ed25519.sign(account.getKeyPair(), message); } catch (Exception e) { e.printStackTrace(); return new byte[64]; } } public boolean verify(byte[] publicKey, byte[] signature, byte[] message) { try { //VERIFY SIGNATURE return Ed25519.verify(signature, message, publicKey); } catch(Exception e) { e.printStackTrace(); return false; } } }