/*
 * 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/rsa.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/buffer.h>
#include <jni.h>
#include "org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL.h"

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

#include "openSSLNative.h"

/*
 * Class:     org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL
 * Method:    generateRSAKeyOpenSSL
 * Signature: (IJ[I)J
 */
JNIEXPORT jlong JNICALL Java_org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_generateRSAKeyOpenSSL
(JNIEnv *env, jclass jobj, jint bits, jlong pubExpo, jintArray lengths) {

    jint *lengthsArray;
    jclass clazz;

    RSA *key = RSA_generate_key(bits, (unsigned long)pubExpo, NULL, NULL);

    if (key == NULL) {
        clazz = (*env)->FindClass(env, RUNTIME_EX);
        (*env)->ThrowNew(env, clazz, "JuICE OpenSSL: Failed to generate RSA 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_RSAKeyPairGeneratorOpenSSL_N_OFFSET] = BN_num_bytes(key->n);
    lengthsArray[org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_E_OFFSET] = BN_num_bytes(key->e);
    lengthsArray[org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_D_OFFSET] = BN_num_bytes(key->d);
    lengthsArray[org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_P_OFFSET] = BN_num_bytes(key->p);
    lengthsArray[org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_Q_OFFSET] = BN_num_bytes(key->q);
    lengthsArray[org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_DP_OFFSET] = BN_num_bytes(key->dmp1);
    lengthsArray[org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_DQ_OFFSET] = BN_num_bytes(key->dmq1);
    lengthsArray[org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_QINV_OFFSET] = BN_num_bytes(key->iqmp);

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

/*
 * Class:     org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL
 * Method:    getRSAKeyOpenSSL
 * Signature: ([B[B[B[B[B[B[B[B)V
 */
JNIEXPORT void JNICALL Java_org_apache_security_juice_generators_RSAKeyPairGeneratorOpenSSL_getRSAKeyOpenSSL
(JNIEnv * env, jclass jobj, jlong handle, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q, 
 jbyteArray dmp1, jbyteArray dmq1, jbyteArray iqmp) {

    jbyte *nb, *eb, *db, *pb, *qb, *dmp1b, *dmq1b, *iqmpb;

#if SIZEOF_INT_P == 4
    RSA *key = (RSA *)((int)(handle & 0xffffffff));
#else
    RSA *key = (RSA *)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)
     */

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

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

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

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

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

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

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

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

