/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <jni.h>
#include "crypto_box_curve25519xsalsa20poly1305.h"
#include "crypto_sign_edwards25519sha512batch.h"

// Lto/yp/cr/NaCl;.moose ()I
JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_moose
  (JNIEnv *env, jobject obj)
{
  return 1;
}


JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_nativeRandomBytes
(JNIEnv *env, jobject obj, jbyteArray bytes)
{
  int l=(*env)->GetArrayLength(env, bytes);
  if (l<1) return -1;
  jbyte *b = (*env)->GetPrimitiveArrayCritical(env, bytes, NULL);

  urandombytes(b,l);

  if (b) (*env)->ReleasePrimitiveArrayCritical(env, bytes, b, 0);
  return 0;
}

/* ------------------------------------------------------------------------
   crypto_box functions : Diffie-Hellman negotiated symmetric cryptography

   CryptoBoxKeypair - generate key pair.
   CryptoBox - Create authenticated an encrypted message.
   CryptoBoxOpen - Decode and verify an en encrypted message.
   ------------------------------------------------------------------------ */

// Lto/yp/cr/NaCl$CryptoBoxKeypair;.method ([B[B)I
JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_nativeCryptoBoxKeypair
  (JNIEnv *env, jobject obj, jbyteArray jsk, jbyteArray jpk)
{
  unsigned char pk[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES];
  unsigned char sk[crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES];

  if (crypto_box_curve25519xsalsa20poly1305_keypair(pk,sk)) return 1;

  /* Set java side versions of pk and sk */
  (*env)->SetByteArrayRegion(env, jpk, 0, crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES, pk);
  (*env)->SetByteArrayRegion(env, jsk, 0, crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES, sk);

  return 0;
}

JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_nativeCryptoBox
(JNIEnv *env, jobject obj, jbyteArray jpk, jbyteArray jsk,jbyteArray jn,jbyteArray jm,jint jmlen,jbyteArray jc)
{
  int i;
  
  if ((*env)->GetArrayLength(env, jpk)!=crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES) return -2;
  if ((*env)->GetArrayLength(env, jsk)!=crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES) return -3;
  if ((*env)->GetArrayLength(env, jn)!=crypto_box_curve25519xsalsa20poly1305_NONCEBYTES) return -4;
  if ((*env)->GetArrayLength(env, jm)!=jmlen) return -5;
  if ((*env)->GetArrayLength(env, jc)!=jmlen) return -6;

  
  /* Get inputs */
  jbyte *pk = (*env)->GetPrimitiveArrayCritical(env, jpk, NULL);
  jbyte *sk = (*env)->GetPrimitiveArrayCritical(env, jsk, NULL);
  jbyte *n = (*env)->GetPrimitiveArrayCritical(env, jn, NULL);
  jbyte *m = (*env)->GetPrimitiveArrayCritical(env, jm, NULL);
  jbyte *c = (*env)->GetPrimitiveArrayCritical(env, jc, NULL);
  
  int r=-1;
  
  if (pk&&sk&&n&&m&&c&&(jmlen>=0&&jmlen<=1048576))
    {
      /* Make sure that space for authenticator is zeroed */
      for(i=0;i<crypto_box_curve25519xsalsa20poly1305_ZEROBYTES;i++)
	{ if (m[i]) return -7; }

      r=crypto_box_curve25519xsalsa20poly1305(c,m,jmlen,n,pk,sk);
    }
  
  /* do these really keep any changes made? */
  if (pk) (*env)->ReleasePrimitiveArrayCritical(env, jpk, pk, 0);
  if (sk) (*env)->ReleasePrimitiveArrayCritical(env, jsk, sk, 0);
  if (n) (*env)->ReleasePrimitiveArrayCritical(env, jn, n, 0);
  if (m) (*env)->ReleasePrimitiveArrayCritical(env, jm, m, 0);
  if (c) (*env)->ReleasePrimitiveArrayCritical(env, jc, c, 0);
  
  return r;
}

JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_nativeCryptoBoxOpen
(JNIEnv *env, jobject obj, jbyteArray jpk, jbyteArray jsk,jbyteArray jn,jbyteArray jm,jint jclen,jbyteArray jc)
{
  int i;
  
  if ((*env)->GetArrayLength(env, jpk)!=crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES) return -2;
  if ((*env)->GetArrayLength(env, jsk)!=crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES) return -3;
  if ((*env)->GetArrayLength(env, jn)!=crypto_box_curve25519xsalsa20poly1305_NONCEBYTES) return -4;
  if ((*env)->GetArrayLength(env, jm)!=jclen) return -5;
  if ((*env)->GetArrayLength(env, jc)!=jclen) return -6;
  
  /* Get inputs */
  jbyte *pk = (*env)->GetPrimitiveArrayCritical(env, jpk, NULL);
  jbyte *sk = (*env)->GetPrimitiveArrayCritical(env, jsk, NULL);
  jbyte *n = (*env)->GetPrimitiveArrayCritical(env, jn, NULL);
  jbyte *m = (*env)->GetPrimitiveArrayCritical(env, jm, NULL);
  jbyte *c = (*env)->GetPrimitiveArrayCritical(env, jc, NULL);
  
  int r=-1;
  
  if (pk&&sk&&n&&m&&c&&(jclen>=0&&jclen<=1048576))
    {
      /* Make sure that space for authenticator is free */
      for(i=0;i<crypto_box_curve25519xsalsa20poly1305_ZEROBYTES;i++)
	{ if (m[i]) return -7; }

      r=crypto_box_curve25519xsalsa20poly1305_open(m,c,jclen,n,pk,sk);
    }
  
  /* do these really keep any changes made? */
  if (pk) (*env)->ReleasePrimitiveArrayCritical(env, jpk, pk, 0);
  if (sk) (*env)->ReleasePrimitiveArrayCritical(env, jsk, sk, 0);
  if (n) (*env)->ReleasePrimitiveArrayCritical(env, jn, n, 0);
  if (m) (*env)->ReleasePrimitiveArrayCritical(env, jm, m, 0);
  if (c) (*env)->ReleasePrimitiveArrayCritical(env, jc, c, 0);
  
  return r;
}


/* ------------------------------------------------------------------------
   crypto_sign functions : Public key cryptography functions

   ------------------------------------------------------------------------ */
JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_nativeCryptoSignKeypair
  (JNIEnv *env, jobject obj, jbyteArray jsk, jbyteArray jpk)
{
  unsigned char pk[crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES];
  unsigned char sk[crypto_sign_edwards25519sha512batch_SECRETKEYBYTES];

  if (crypto_sign_edwards25519sha512batch_keypair(pk,sk)) return 1;

  /* Set java side versions of pk and sk */
  (*env)->SetByteArrayRegion(env, jpk, 0, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES, pk);
  (*env)->SetByteArrayRegion(env, jsk, 0, crypto_sign_edwards25519sha512batch_SECRETKEYBYTES, sk);

  return 0;
}

JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_nativeCryptoSign
(JNIEnv *env, jobject obj, jbyteArray jsk,jbyteArray jm,jbyteArray jsm)
{
  long long mlen,smlen,smlen_in;

  smlen=(*env)->GetArrayLength(env, jsm);
  smlen_in=smlen;
  mlen=(*env)->GetArrayLength(env, jm);

  if ((smlen-mlen)!=crypto_sign_edwards25519sha512batch_BYTES) return -4;  
  if ((*env)->GetArrayLength(env, jsk)!=crypto_sign_edwards25519sha512batch_SECRETKEYBYTES) return -3;
  
  /* Get inputs */
  jbyte *sk = (*env)->GetPrimitiveArrayCritical(env, jsk, NULL);
  jbyte *m = (*env)->GetPrimitiveArrayCritical(env, jm, NULL);
  jbyte *sm = (*env)->GetPrimitiveArrayCritical(env, jsm, NULL);
  
  int r=-1;

  if (sk&&m&&sm)
    {
      r=crypto_sign_edwards25519sha512batch_ref(sm,&smlen,m,mlen,sk);
      if (smlen!=smlen_in) return -5;
    }
  
  /* do these really keep any changes made? */
  if (sk) (*env)->ReleasePrimitiveArrayCritical(env, jsk, sk, 0);
  if (m) (*env)->ReleasePrimitiveArrayCritical(env, jm, m, 0);
  if (sm) (*env)->ReleasePrimitiveArrayCritical(env, jsm, sm, 0);
  
  return r;
}

JNIEXPORT jint JNICALL Java_to_yp_cr_NaCl_nativeCryptoSignOpen
(JNIEnv *env, jobject obj, jbyteArray jpk,jbyteArray jsm,jbyteArray jm)
{
  long long mlen,smlen,mlen_in;

  smlen=(*env)->GetArrayLength(env, jsm);
  mlen=(*env)->GetArrayLength(env, jm); mlen_in=mlen;

  if ((smlen-mlen)!=crypto_sign_edwards25519sha512batch_BYTES) return -4;  
  if ((*env)->GetArrayLength(env, jpk)!=crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES) return -3;
  
  /* Get inputs */
  jbyte *pk = (*env)->GetPrimitiveArrayCritical(env, jpk, NULL);
  jbyte *m = (*env)->GetPrimitiveArrayCritical(env, jm, NULL);
  jbyte *sm = (*env)->GetPrimitiveArrayCritical(env, jsm, NULL);
  
  int r=-1;

  if (pk&&m&&sm)
    {
      r=crypto_sign_edwards25519sha512batch_open(m,&mlen,sm,smlen,pk);
      if (mlen!=mlen_in) return -5;
    }
  
  /* do these really keep any changes made? */
  if (pk) (*env)->ReleasePrimitiveArrayCritical(env, jpk, pk, 0);
  if (m) (*env)->ReleasePrimitiveArrayCritical(env, jm, m, 0);
  if (sm) (*env)->ReleasePrimitiveArrayCritical(env, jsm, sm, 0);
  
  return r;
}