/*
 * 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 <stdio.h>
#include <openssl/evp.h>
#include <openssl/dh.h>
#include <openssl/bn.h>
#include <openssl/buffer.h>
#include <jni.h>
#include "org_apache_security_juice_generators_DHParametersGeneratorOpenSSL.h"

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

#include "openSSLNative.h"


/*
 * Class:     org_apache_security_juice_generators_DHParametersGeneratorOpenSSL
 * Method:    generateParamDHOpenSSL
 * Signature: (I[I)J
 */
JNIEXPORT jlong JNICALL Java_org_apache_security_juice_generators_DHParametersGeneratorOpenSSL_generateParamDHOpenSSL
(JNIEnv * env, jobject jobj, jint size, jintArray lengths) {

    jclass clazz;
    DH *key;
    int ret, cause;
    jint *lengthsArray;

    key = DH_generate_parameters(size, 2, NULL, NULL);

    if (key == NULL) {
        clazz = (*env)->FindClass(env, RUNTIME_EX);
        (*env)->ThrowNew(env, clazz, getErrorStringOpenSSL("JuiCE OpenSSL: Failed to generate Diffie-Helman parameters"));
        return 0L;
    }
    
    ret = DH_check(key, &cause);

    if (ret == 0) {
        clazz = (*env)->FindClass(env, RUNTIME_EX);
	if ((cause & DH_CHECK_P_NOT_SAFE_PRIME) == DH_CHECK_P_NOT_SAFE_PRIME) {
	    (*env)->ThrowNew(env, clazz, getErrorStringOpenSSL("JuiCE OpenSSL: Failed to generate Diffie-Helman parameters - no save prime found"));
	} else if ((cause & DH_NOT_SUITABLE_GENERATOR) == DH_NOT_SUITABLE_GENERATOR) {
	    (*env)->ThrowNew(env, clazz, getErrorStringOpenSSL("JuiCE OpenSSL: Failed to generate Diffie-Helman parameters - no suitable generator"));
	}
	else {
	    (*env)->ThrowNew(env, clazz, getErrorStringOpenSSL("JuiCE OpenSSL: Failed to generate Diffie-Helman parameters - unable to check generator"));
	}
	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 getParamsDHOpenSSL(...)
     */
    lengthsArray[org_apache_security_juice_generators_DHParametersGeneratorOpenSSL_P_OFFSET] = BN_num_bytes(key->p);
    lengthsArray[org_apache_security_juice_generators_DHParametersGeneratorOpenSSL_G_OFFSET] = BN_num_bytes(key->g);

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

}

/*
 * Class:     org_apache_security_juice_generators_DHParametersGeneratorOpenSSL
 * Method:    getParamsDHOpenSSL
 * Signature: (J[B[B)V
 */
JNIEXPORT void JNICALL Java_org_apache_security_juice_generators_DHParametersGeneratorOpenSSL_getParamsDHOpenSSL
(JNIEnv *env, jobject jobj, jlong handle, jbyteArray p, jbyteArray g) {

    jbyte *pb, *gb;

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

    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);

    DH_free(key);
}

