package org.ripple.power; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.util.HashSet; import java.util.zip.CRC32; import org.ripple.power.config.LSystem; import org.ripple.power.utils.MathUtils; import com.ripple.config.Config; import com.ripple.crypto.ecdsa.SECP256K1; import com.ripple.utils.Utils; public class NativeSupport { public static boolean isWindows = System.getProperty("os.name").contains( "Windows"); public static boolean isLinux = System.getProperty("os.name").contains( "Linux"); public static boolean isMac = System.getProperty("os.name").contains("Mac"); public static boolean isAndroid = false; public static boolean is64Bit = System.getProperty("os.arch").equals( "amd64"); private static HashSet<String> loadedLibraries = new HashSet<String>(); public static String CRC(InputStream input) { if (input == null) { return "" + System.nanoTime(); } CRC32 crc = new CRC32(); byte[] buffer = new byte[4096]; try { while (true) { int length = input.read(buffer); if (length == -1) break; crc.update(buffer, 0, length); } } catch (Exception ex) { try { input.close(); } catch (Exception ignored) { } } return Long.toString(crc.getValue()); } public static String libNames(String libraryName) { if (isWindows) { return libraryName + (is64Bit ? "64.dll" : ".dll"); } if (isLinux) { return "lib" + libraryName + (is64Bit ? "64.so" : ".so"); } if (isMac) { return "lib" + libraryName + ".dylib"; } return libraryName; } public static synchronized void loadJNI(String libraryName) throws Throwable { libraryName = libNames(libraryName); if (loadedLibraries.contains(libraryName)) { return; } try { if (isAndroid) { System.loadLibrary(libraryName); } else { System.load(export(libraryName, null).getAbsolutePath()); } } catch (Throwable ex) { throw new Exception(ex); } loadedLibraries.add(libraryName); } public static File export(String sourcePath, String dirName) throws IOException { return export(sourcePath, dirName, null); } public static File export(String sourcePath, String dirName, String name) throws IOException { ClassLoader loader = NativeSupport.class.getClassLoader(); String sourceCrc = CRC(loader.getResourceAsStream(sourcePath)); if (dirName == null) { dirName = sourceCrc; } File extractedDir = new File(System.getProperty("java.io.tmpdir") + "/loon" + LSystem.getUserName() + "/" + dirName); File extractedFile = new File(extractedDir, name == null ? new File( sourcePath).getName() : name); String extractedCrc = null; if (extractedFile.exists()) { try { extractedCrc = CRC(new FileInputStream(extractedFile)); } catch (FileNotFoundException ignored) { } } if (extractedCrc == null || !extractedCrc.equals(sourceCrc)) { try { InputStream input = loader.getResourceAsStream(sourcePath); if (input == null) { return null; } extractedDir.mkdirs(); FileOutputStream output = new FileOutputStream(extractedFile); byte[] buffer = new byte[4096]; while (true) { int length = input.read(buffer); if (length == -1) break; output.write(buffer, 0, length); } input.close(); output.close(); } catch (IOException ex) { throw new RuntimeException("Error extracting file: " + sourcePath, ex); } } return extractedFile.exists() ? extractedFile : null; } private static boolean nativesLoaded; public static final int SIZEOF_BYTE = 1; public static final int SIZEOF_SHORT = 2; public static final int SIZEOF_FLOAT = 4; public static final int SIZEOF_INT = SIZEOF_FLOAT; public static final int SIZEOF_DOUBLE = 8; public static final int SIZEOF_LONG = SIZEOF_DOUBLE; private static boolean useLoonNative; static { String vm = System.getProperty("java.vm.name"); if (vm != null && vm.contains("Dalvik")) { isAndroid = true; isWindows = false; isLinux = false; isMac = false; is64Bit = false; } try { loadJNI("jcoin"); useLoonNative = true; System.out.println("Support of the native method call"); } catch (Throwable e) { useLoonNative = false; } } public static boolean UseLoonNative() { return useLoonNative; } public static void CloseLoonNative() { useLoonNative = false; } private static native String getByteKeys(byte[] bytes, boolean compressed); private static native String getHashKeys(byte[] bytes, boolean compressed); private static native byte[] getNxtHashKeys(byte[] bytes); private static native boolean findByteAddress(byte[] dst, byte[] src); private static native String getRippleBase58(byte[] bytes); private static native byte[] getRipemd160Sha256(byte[] dst); private static native String getSecp256k1ToPublic(byte[] dst); private static native byte[] getSha512Quarter(byte[] dst); private static native byte[] getSha512Half(byte[] dst); public static byte[] toRipemd160Sha256(byte[] dst) { if (useLoonNative) { try { return getRipemd160Sha256(dst); } catch (Throwable ex) { return CoinUtils.sha256ripemd160(dst); } } else { return CoinUtils.sha256ripemd160(dst); } } public static byte[] getNxtKey(String secret) { byte[] publicKeyHash = null; try { publicKeyHash = secret.getBytes(LSystem.encoding); } catch (UnsupportedEncodingException e) { return null; } if (useLoonNative) { return getNxtHashKeys(publicKeyHash); } else { byte[] publicKey = new byte[32]; Curve25519.keygen(publicKey, Helper.update(publicKeyHash)); publicKeyHash = Helper.update(publicKey); } return publicKeyHash; } public static String getBitcoinPrivateKey(String secret) { return getBitcoinPrivateKey(secret, false); } public static String getBitcoinPrivateKey(String secret, boolean compressed) { if (useLoonNative) { try { return getByteKeys(secret.getBytes(LSystem.encoding), compressed); } catch (Exception e) { return ""; } } else { byte[] hash = null; try { hash = Helper.update(secret.getBytes(LSystem.encoding)); } catch (UnsupportedEncodingException e) { return ""; } byte[] pub = CoinUtils.generatePublicKey(new BigInteger(1, hash), compressed); boolean c = (pub.length == 33); byte[] bytes = hash; if (c) { bytes = new byte[33]; System.arraycopy(hash, 0, bytes, 0, 32); bytes[32] = 1; } int size = bytes.length; byte[] addressBytes = new byte[1 + size + 4]; // type 0 is 128 addressBytes[0] = ((byte) 128); System.arraycopy(bytes, 0, addressBytes, 1, size); byte[] check = Helper.doubleDigest(addressBytes, 0, size + 1); System.arraycopy(check, 0, addressBytes, size + 1, 4); return CoinUtils.publicKeyToAddress(pub) + "," + CoinUtils.encodeBase58(addressBytes); } } public static String getBitcoinBigIntegerPrivateKey(BigInteger id, boolean compressed) { String hashString = MathUtils.addZeros( CoinUtils.toHex(id.toByteArray()), 64); byte[] hash = CoinUtils.fromHex(hashString); if (useLoonNative) { try { return getHashKeys(hash, compressed); } catch (Exception e) { return ""; } } else { byte[] pub = CoinUtils.generatePublicKey(new BigInteger(hash), compressed); boolean c = (pub.length == 33); byte[] bytes = hash; if (c) { bytes = new byte[33]; System.arraycopy(hash, 0, bytes, 0, 32); bytes[32] = 1; } int size = bytes.length; byte[] addressBytes = new byte[1 + size + 4]; // type 0 is 128 addressBytes[0] = ((byte) 128); System.arraycopy(bytes, 0, addressBytes, 1, size); byte[] check = Helper.doubleDigest(addressBytes, 0, size + 1); System.arraycopy(check, 0, addressBytes, size + 1, 4); return CoinUtils.publicKeyToAddress(pub) + "," + CoinUtils.encodeBase58(addressBytes); } } public static String getBitcoinBigIntegerPrivateKey(String hashString, boolean compressed) { byte[] hash = CoinUtils.fromHex(hashString); if (useLoonNative) { try { return getHashKeys(hash, compressed); } catch (Exception e) { return ""; } } else { byte[] pub = CoinUtils.generatePublicKey(new BigInteger(hash), compressed); boolean c = (pub.length == 33); byte[] bytes = hash; if (c) { bytes = new byte[33]; System.arraycopy(hash, 0, bytes, 0, 32); bytes[32] = 1; } int size = bytes.length; byte[] addressBytes = new byte[1 + size + 4]; // type 0 is 128 addressBytes[0] = ((byte) 128); System.arraycopy(bytes, 0, addressBytes, 1, size); byte[] check = Helper.doubleDigest(addressBytes, 0, size + 1); System.arraycopy(check, 0, addressBytes, size + 1, 4); return CoinUtils.publicKeyToAddress(pub) + "," + CoinUtils.encodeBase58(addressBytes); } } private static native String getRippleByteKeys(byte[] seedBytes); private static native byte[] getRippleHashKeys(byte[] seedBytes); private static native String[] getRippleBatchKeys(byte[] res, int max); public static byte[] createKeyPair(byte[] seedBytes) { BigInteger secret, pub, privateGen, order = SECP256K1.order(); byte[] privateGenBytes; byte[] publicGenBytes; int i = 0, seq = 0; for (;;) { privateGenBytes = hashedIncrement(seedBytes, i++); privateGen = Utils.uBigInt(privateGenBytes); if (privateGen.compareTo(order) == -1) { break; } } publicGenBytes = CoinUtils.generateKey(privateGen); i = 0; for (;;) { byte[] secretBytes = hashedIncrement( appendIntBytes(publicGenBytes, seq), i++); secret = Utils.uBigInt(secretBytes); if (secret.compareTo(order) == -1) { break; } } secret = secret.add(privateGen).mod(order); pub = Utils.uBigInt(CoinUtils.generateKey(secret)); return pub.toByteArray(); } private static byte[] hashedIncrement(byte[] bytes, int increment) { if (useLoonNative) { try { return getSha512Half(appendIntBytes(bytes, increment)); } catch (Throwable t) { return Helper.halfSHA512(appendIntBytes(bytes, increment)); } } else { return Helper.halfSHA512(appendIntBytes(bytes, increment)); } } private static byte[] appendIntBytes(byte[] in, int i) { byte[] out = new byte[in.length + 4]; System.arraycopy(in, 0, out, 0, in.length); out[in.length] = (byte) ((i >>> 24) & 0xFF); out[in.length + 1] = (byte) ((i >>> 16) & 0xFF); out[in.length + 2] = (byte) ((i >>> 8) & 0xFF); out[in.length + 3] = (byte) ((i) & 0xFF); return out; } public static String getRipplePrivateKey(String secret) { if (useLoonNative) { try { return getRippleByteKeys(secret.getBytes(LSystem.encoding)); } catch (Throwable t) { try { byte[] master = Helper.quarterSha512(secret .getBytes(LSystem.encoding)); String seed = Config.getB58IdentiferCodecs() .encodeFamilySeed(master); return new RipplePublicKey(createKeyPair(master)) .getAddress().toString() + "," + seed; } catch (Exception ex) { return ""; } } } else { try { byte[] master = Helper.quarterSha512(secret .getBytes(LSystem.encoding)); String seed = Config.getB58IdentiferCodecs().encodeFamilySeed( master); return new RipplePublicKey(createKeyPair(master)).getAddress() .toString() + "," + seed; } catch (Exception e) { return ""; } } } public static String getRippleBigIntegerPrivateKey(BigInteger id) { String hashString = MathUtils.addZeros( CoinUtils.toHex(id.toByteArray()), 32); byte[] hash = CoinUtils.fromHex(hashString); if (useLoonNative) { try { return new String(getRippleHashKeys(hash)); } catch (Throwable t) { try { String seed = Config.getB58IdentiferCodecs() .encodeFamilySeed(hash); return new RipplePublicKey(createKeyPair(hash)) .getAddress().toString() + "," + seed; } catch (Exception e) { return ""; } } } else { try { String seed = Config.getB58IdentiferCodecs().encodeFamilySeed( hash); return new RipplePublicKey(createKeyPair(hash)).getAddress() .toString() + "," + seed; } catch (Exception e) { return ""; } } } public static String getRippleBigIntegerPrivateKey(String hashString) { byte[] hash = CoinUtils.fromHex(hashString); if (useLoonNative) { try { return new String(getRippleHashKeys(hash)); } catch (Throwable t) { try { String seed = Config.getB58IdentiferCodecs() .encodeFamilySeed(hash); return new RipplePublicKey(createKeyPair(hash)) .getAddress().toString() + "," + seed; } catch (Exception e) { return ""; } } } else { try { String seed = Config.getB58IdentiferCodecs().encodeFamilySeed( hash); return new RipplePublicKey(createKeyPair(hash)).getAddress() .toString() + "," + seed; } catch (Exception e) { return ""; } } } public static String[] getRippleBatch(BigInteger id, int max) { String hashString = MathUtils.addZeros( CoinUtils.toHex(id.toByteArray()), 32); byte[] hash = CoinUtils.fromHex(hashString); if (useLoonNative) { try { return getRippleBatchKeys(hash, max); } catch (Throwable t) { try { String[] lists = new String[max]; BigInteger big = new BigInteger(hash); for (int i = 0; i < max; i++) { lists[i] = getRippleBigIntegerPrivateKey(big); big = big.add(BigInteger.ONE); } return lists; } catch (Exception e) { return null; } } } else { try { String[] lists = new String[max]; BigInteger big = new BigInteger(hash); for (int i = 0; i < max; i++) { lists[i] = getRippleBigIntegerPrivateKey(big); big = big.add(BigInteger.ONE); } return lists; } catch (Exception e) { return null; } } } public static String getRippleBigIntegerPrivateKeys(BigInteger id) { String hashString = MathUtils.addZeros( CoinUtils.toHex(id.toByteArray()), 32); byte[] hash = CoinUtils.fromHex(hashString); if (useLoonNative) { try { return new String(getRippleHashKeys(hash)); } catch (Throwable t) { try { String seed = Config.getB58IdentiferCodecs() .encodeFamilySeed(hash); return new RipplePublicKey(createKeyPair(hash)) .getAddress().toString() + "," + seed; } catch (Exception e) { return ""; } } } else { try { String seed = Config.getB58IdentiferCodecs().encodeFamilySeed( hash); return new RipplePublicKey(createKeyPair(hash)).getAddress() .toString() + "," + seed; } catch (Exception e) { return ""; } } } private final static byte _tag = '\n'; public final static boolean findCoinAddress(byte[] dst, byte[] src) { if (useLoonNative) { return findByteAddress(dst, src); } else { int patternOffset = 0; int len = dst.length; int index = 0; byte b = src[index++]; int size = src.length; int mark = 0; for (; index < size;) { if (dst[patternOffset] == b) { patternOffset++; if (patternOffset == len) { return true; } } else { if (b == _tag) { mark = index; } else { int skip = index - mark; int go = 34 - skip; if (go + index < size && src[go + index] == _tag) { index = index + go; } else if ((go + index - 1) < size && src[(go + index - 1)] == _tag) { index = (index + go - 1); } } patternOffset = 0; } b = src[index++]; } return false; } } }