package im.actor.runtime.crypto; import im.actor.runtime.Crypto; import im.actor.runtime.crypto.primitives.curve25519.Sha512; import im.actor.runtime.crypto.primitives.curve25519.curve_sigs; import im.actor.runtime.crypto.primitives.curve25519.scalarmult; import im.actor.runtime.crypto.primitives.digest.SHA512; // Disabling Bounds checks for speeding up calculations /*-[ #define J2OBJC_DISABLE_ARRAY_BOUND_CHECKS 1 ]-*/ public final class Curve25519 { /** * Generating KeyPair * * @param randomBytes 32 random bytes * @return generated key pair */ public static Curve25519KeyPair keyGen(byte[] randomBytes) { byte[] privateKey = keyGenPrivate(randomBytes); byte[] publicKey = keyGenPublic(privateKey); return new Curve25519KeyPair(publicKey, privateKey); } /** * Generating private key. Source: https://cr.yp.to/ecdh.html * * @param randomBytes random bytes (32+ bytes) * @return generated private key */ public static byte[] keyGenPrivate(byte[] randomBytes) { if (randomBytes.length < 32) { throw new RuntimeException("Random bytes too small"); } // Hashing Random Bytes instead of using random bytes directly // Just in case as reference ed255519 implementation do same byte[] privateKey = new byte[32]; Digest sha256 = Crypto.createSHA256(); sha256.update(randomBytes, 0, randomBytes.length); sha256.doFinal(privateKey, 0); // Performing bit's flipping privateKey[0] &= 248; privateKey[31] &= 127; privateKey[31] |= 64; return privateKey; } /** * Building public key with private key * * @param privateKey private key * @return generated public key */ public static byte[] keyGenPublic(byte[] privateKey) { byte[] publicKey = new byte[32]; curve_sigs.curve25519_keygen(publicKey, privateKey); return publicKey; } /** * Calculating DH agreement * * @param ourPrivate Our Private Key * @param theirPublic Theirs Public key * @return calculated agreement */ public static byte[] calculateAgreement(byte[] ourPrivate, byte[] theirPublic) { byte[] agreement = new byte[32]; scalarmult.crypto_scalarmult(agreement, ourPrivate, theirPublic); return agreement; } /** * Calculating signature * * @param random random seed for signature * @param privateKey private key for signature * @param message message to sign * @return signature */ public static byte[] calculateSignature(byte[] random, byte[] privateKey, byte[] message) { byte[] result = new byte[64]; if (curve_sigs.curve25519_sign(SHA512Provider, result, privateKey, message, message.length, random) != 0) { throw new IllegalArgumentException("Message exceeds max length!"); } return result; } /** * Verification of signature * * @param publicKey public key of signature * @param message message * @param signature signature of a message * @return true if signature correct */ public static boolean verifySignature(byte[] publicKey, byte[] message, byte[] signature) { return curve_sigs.curve25519_verify(SHA512Provider, signature, publicKey, message, message.length) == 0; } private static final Sha512 SHA512Provider = new Sha512() { @Override public void calculateDigest(byte[] out, byte[] in, long length) { SHA512 sha512 = new SHA512(); sha512.update(in, 0, (int) length); sha512.doFinal(out, 0); } }; private Curve25519() { } }