/*
 * 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_provider_JCEDHKeyAgreementOpenSSL.h"

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

#include "openSSLNative.h"

/*
 * Class:     org_apache_security_juice_provider_JCEDHKeyAgreementOpenSSL
 * Method:    doPhaseOpenSSL
 * Signature: ([BI[BI[BI[BI)[B
 */
JNIEXPORT jbyteArray JNICALL Java_org_apache_security_juice_provider_JCEDHKeyAgreementOpenSSL_doPhaseDHOpenSSL
(JNIEnv *env, jobject jobj, jbyteArray p, jint plen, jbyteArray g, jint glen, jbyteArray x, jint xlen, jbyteArray y, jint ylen) {

    jbyte *pb, *gb, *xb, *yb, *result;
    int size, ret;
    jclass clazz;
    jbyteArray javaReturn;

    DH *key = DH_new();

    if (key == NULL) {
	JUICEerr(F_doPhaseDHOpenSSL, ERR_R_MALLOC_FAILURE);
	clazz = (*env)->FindClass(env, OUT_OF_MEMORY_EX);
        (*env)->ThrowNew(env, clazz, getErrorStringOpenSSL(OUT_OF_MEMORY_MSG));
	return NULL;
    }

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

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

    xb = (*env)->GetByteArrayElements(env, x, 0);
    if (xb == NULL) {
	return NULL;
    }
    key->priv_key = BN_bin2bn(xb, xlen, NULL);
    (*env)->ReleaseByteArrayElements(env, x, xb, JNI_ABORT);

    yb = (*env)->GetByteArrayElements(env, y, 0);
    if (yb == NULL) {
	return NULL;
    }
    key->pub_key = BN_bin2bn(yb, ylen, NULL);
    (*env)->ReleaseByteArrayElements(env, y, yb, JNI_ABORT);

    size = DH_size(key);

    javaReturn = (*env)->NewByteArray(env, size);
    if (javaReturn == NULL) {
	return NULL;
    }

    result = (*env)->GetPrimitiveArrayCritical(env, javaReturn, 0);
    if (result == NULL) {
	DH_free(key);
	return NULL;
    }
    ret = DH_compute_key(result, key->pub_key, key);
    (*env)->ReleasePrimitiveArrayCritical(env, javaReturn, result, 0);

    if(ret == 0) {
	DH_free(key);
        clazz = (*env)->FindClass(env, RUNTIME_EX);
        (*env)->ThrowNew(env, clazz, getErrorStringOpenSSL("JuiCE OpenSSL: Diffie-Helman compute secret key failed"));
        return NULL;
    }

    return javaReturn;
}

