content: 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() {
  
      }
  }