/*
 * Copyright 2002-2006 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * imitations under the License.
 */

#include <openssl/evp.h>
#include <openssl/dsa.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/buffer.h>
#include <jni.h>
#include "org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "openSSLNative.h"

/*
 * Class:     org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL
 * Method:    generateDSAKeyOpenSSL
 * Signature: (IJ[I)J
 */
JNIEXPORT jlong JNICALL Java_org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL_generateDSAKeyOpenSSL
(JNIEnv *env, jclass jobj, jint bits, jintArray lengths) {

    jint *lengthsArray;
    jclass clazz;
    int ret;

    DSA *key = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL);

    if (key == NULL) {
        clazz = (*env)->FindClass(env, RUNTIME_EX);
        (*env)->ThrowNew(env, clazz, "JuICE OpenSSL: Failed to generate DSA parameters");
	return 0L;
    }

    ret = DSA_generate_key(key);
    if (ret == 0) {
        clazz = (*env)->FindClass(env, RUNTIME_EX);
        (*env)->ThrowNew(env, clazz, "JuICE OpenSSL: Failed to generate DSA key");
	return 0L;
    }
    lengthsArray = (*env)->GetIntArrayElements(env, lengths, 0);
    if (lengthsArray == NULL) {
        return 0L;
    }
	
    /*
     * Get the lengths of the generated big numbers and return
     * the lengths to caller. The caller may now allocate enough space to
     * get them using getRSAKeyOpenSSL(...)
     */
    lengthsArray[org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL_Q_OFFSET] = BN_num_bytes(key->q);
    lengthsArray[org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL_P_OFFSET] = BN_num_bytes(key->p);
    lengthsArray[org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL_G_OFFSET] = BN_num_bytes(key->g);
    lengthsArray[org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL_PRIV_OFFSET] = BN_num_bytes(key->priv_key);
    lengthsArray[org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL_PUB_OFFSET] = BN_num_bytes(key->pub_key);

    (*env)->ReleaseIntArrayElements(env, lengths, lengthsArray, 0);
    return (unsigned long)key;
}

/*
 * Class:     org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL
 * Method:    getDSAKeyOpenSSL
 * Signature: ([B[B[B[B[B)V
 */
JNIEXPORT void JNICALL Java_org_apache_security_juice_generators_DSAKeyPairGeneratorOpenSSL_getDSAKeyOpenSSL
(JNIEnv * env, jclass jobj, jlong handle, jbyteArray q, jbyteArray p, jbyteArray g, jbyteArray priv, jbyteArray pub) {

    jbyte *qb, *pb, *gb, *privb, *pubb;

#if SIZEOF_INT_P == 4
    DSA *key = (DSA *)((int)(handle & 0xffffffff));
#else
    DSA *key = (DSA *)handle;
#endif

    /*
     * We check if the pointer of the array opeartion is NULL. In that
     * case an "OutOfMemoryError" occured. Return immediatly. The
     * exception will be handled by the java code (according to JNI
     * documentation)
     */

    qb = (*env)->GetByteArrayElements(env, q, 0);
    if (qb == NULL) {
        return;
    }
    BN_bn2bin(key->q, qb);
    (*env)->ReleaseByteArrayElements(env, q, qb, 0);

    pb = (*env)->GetByteArrayElements(env, p, 0);
    if (pb == NULL) {
        return;
    }
    BN_bn2bin(key->p, pb);
    (*env)->ReleaseByteArrayElements(env, p, pb, 0);

    gb = (*env)->GetByteArrayElements(env, g, 0);
    if (gb == NULL) {
	return;
    }
    BN_bn2bin(key->g, gb);
    (*env)->ReleaseByteArrayElements(env, g, gb, 0);

    privb = (*env)->GetByteArrayElements(env, priv, 0);
    if (privb == NULL) {
        return;
    }
    BN_bn2bin(key->priv_key, privb);
    (*env)->ReleaseByteArrayElements(env, priv, privb, 0);

    pubb = (*env)->GetByteArrayElements(env, pub, 0);
    if (pubb == NULL) {
        return;
    }
    BN_bn2bin(key->pub_key, pubb);
    (*env)->ReleaseByteArrayElements(env, pub, pubb, 0);
}

