6840752: Provide out-of-the-box support for ECC algorithms
Reviewed-by: alanb, mullan, wetmore
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#include <jni.h>
#include "ecc_impl.h"
#define ILLEGAL_STATE_EXCEPTION "java/lang/IllegalStateException"
#define INVALID_ALGORITHM_PARAMETER_EXCEPTION \
"java/security/InvalidAlgorithmParameterException"
#define INVALID_PARAMETER_EXCEPTION \
"java/security/InvalidParameterException"
#define KEY_EXCEPTION "java/security/KeyException"
extern "C" {
/*
* Throws an arbitrary Java exception.
*/
void ThrowException(JNIEnv *env, char *exceptionName)
{
jclass exceptionClazz = env->FindClass(exceptionName);
env->ThrowNew(exceptionClazz, NULL);
}
/*
* Deep free of the ECParams struct
*/
void FreeECParams(ECParams *ecparams, jboolean freeStruct)
{
// Use B_FALSE to free the SECItem->data element, but not the SECItem itself
// Use B_TRUE to free both
SECITEM_FreeItem(&ecparams->fieldID.u.prime, B_FALSE);
SECITEM_FreeItem(&ecparams->curve.a, B_FALSE);
SECITEM_FreeItem(&ecparams->curve.b, B_FALSE);
SECITEM_FreeItem(&ecparams->curve.seed, B_FALSE);
SECITEM_FreeItem(&ecparams->base, B_FALSE);
SECITEM_FreeItem(&ecparams->order, B_FALSE);
SECITEM_FreeItem(&ecparams->DEREncoding, B_FALSE);
SECITEM_FreeItem(&ecparams->curveOID, B_FALSE);
if (freeStruct)
free(ecparams);
}
/*
* Class: sun_security_ec_ECKeyPairGenerator
* Method: generateECKeyPair
* Signature: (I[B[B)[J
*/
JNIEXPORT jlongArray
JNICALL Java_sun_security_ec_ECKeyPairGenerator_generateECKeyPair
(JNIEnv *env, jclass clazz, jint keySize, jbyteArray encodedParams, jbyteArray seed)
{
ECPrivateKey *privKey; /* contains both public and private values */
ECParams *ecparams = NULL;
SECKEYECParams params_item;
jint jSeedLength;
jbyte* pSeedBuffer = NULL;
jlongArray result = NULL;
jlong* resultElements = NULL;
// Initialize the ECParams struct
params_item.len = env->GetArrayLength(encodedParams);
params_item.data =
(unsigned char *) env->GetByteArrayElements(encodedParams, 0);
// Fill a new ECParams using the supplied OID
if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) {
/* bad curve OID */
ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
goto cleanup;
}
// Copy seed from Java to native buffer
jSeedLength = env->GetArrayLength(seed);
pSeedBuffer = new jbyte[jSeedLength];
env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
// Generate the new keypair (using the supplied seed)
if (EC_NewKey(ecparams, &privKey, (unsigned char *) pSeedBuffer,
jSeedLength, 0) != SECSuccess) {
ThrowException(env, KEY_EXCEPTION);
goto cleanup;
}
jboolean isCopy;
result = env->NewLongArray(2);
resultElements = env->GetLongArrayElements(result, &isCopy);
resultElements[0] = (jlong) &(privKey->privateValue); // private big integer
resultElements[1] = (jlong) &(privKey->publicValue); // encoded ec point
// If the array is a copy then we must write back our changes
if (isCopy == JNI_TRUE) {
env->ReleaseLongArrayElements(result, resultElements, 0);
}
cleanup:
{
if (params_item.data)
env->ReleaseByteArrayElements(encodedParams,
(jbyte *) params_item.data, JNI_ABORT);
if (ecparams)
FreeECParams(ecparams, true);
if (privKey) {
FreeECParams(&privKey->ecParams, false);
SECITEM_FreeItem(&privKey->version, B_FALSE);
// Don't free privKey->privateValue and privKey->publicValue
}
if (pSeedBuffer)
delete [] pSeedBuffer;
}
return result;
}
/*
* Class: sun_security_ec_ECKeyPairGenerator
* Method: getEncodedBytes
* Signature: (J)[B
*/
JNIEXPORT jbyteArray
JNICALL Java_sun_security_ec_ECKeyPairGenerator_getEncodedBytes
(JNIEnv *env, jclass clazz, jlong hSECItem)
{
SECItem *s = (SECItem *)hSECItem;
jbyteArray jEncodedBytes = env->NewByteArray(s->len);
// Copy bytes from a native SECItem buffer to Java byte array
env->SetByteArrayRegion(jEncodedBytes, 0, s->len, (jbyte *)s->data);
// Use B_FALSE to free only the SECItem->data
SECITEM_FreeItem(s, B_FALSE);
return jEncodedBytes;
}
/*
* Class: sun_security_ec_ECDSASignature
* Method: signDigest
* Signature: ([B[B[B[B)[B
*/
JNIEXPORT jbyteArray
JNICALL Java_sun_security_ec_ECDSASignature_signDigest
(JNIEnv *env, jclass clazz, jbyteArray digest, jbyteArray privateKey, jbyteArray encodedParams, jbyteArray seed)
{
jbyte* pDigestBuffer = NULL;
jint jDigestLength = env->GetArrayLength(digest);
jbyteArray jSignedDigest = NULL;
SECItem signature_item;
jbyte* pSignedDigestBuffer = NULL;
jbyteArray temp;
jint jSeedLength = env->GetArrayLength(seed);
jbyte* pSeedBuffer = NULL;
// Copy digest from Java to native buffer
pDigestBuffer = new jbyte[jDigestLength];
env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
SECItem digest_item;
digest_item.data = (unsigned char *) pDigestBuffer;
digest_item.len = jDigestLength;
ECPrivateKey privKey;
// Initialize the ECParams struct
ECParams *ecparams = NULL;
SECKEYECParams params_item;
params_item.len = env->GetArrayLength(encodedParams);
params_item.data =
(unsigned char *) env->GetByteArrayElements(encodedParams, 0);
// Fill a new ECParams using the supplied OID
if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) {
/* bad curve OID */
ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
goto cleanup;
}
// Extract private key data
privKey.ecParams = *ecparams; // struct assignment
privKey.privateValue.len = env->GetArrayLength(privateKey);
privKey.privateValue.data =
(unsigned char *) env->GetByteArrayElements(privateKey, 0);
// Prepare a buffer for the signature (twice the key length)
pSignedDigestBuffer = new jbyte[ecparams->order.len * 2];
signature_item.data = (unsigned char *) pSignedDigestBuffer;
signature_item.len = ecparams->order.len * 2;
// Copy seed from Java to native buffer
pSeedBuffer = new jbyte[jSeedLength];
env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
// Sign the digest (using the supplied seed)
if (ECDSA_SignDigest(&privKey, &signature_item, &digest_item,
(unsigned char *) pSeedBuffer, jSeedLength, 0) != SECSuccess) {
ThrowException(env, KEY_EXCEPTION);
goto cleanup;
}
// Create new byte array
temp = env->NewByteArray(signature_item.len);
// Copy data from native buffer
env->SetByteArrayRegion(temp, 0, signature_item.len, pSignedDigestBuffer);
jSignedDigest = temp;
cleanup:
{
if (params_item.data)
env->ReleaseByteArrayElements(encodedParams,
(jbyte *) params_item.data, JNI_ABORT);
if (pDigestBuffer)
delete [] pDigestBuffer;
if (pSignedDigestBuffer)
delete [] pSignedDigestBuffer;
if (pSeedBuffer)
delete [] pSeedBuffer;
if (ecparams)
FreeECParams(ecparams, true);
}
return jSignedDigest;
}
/*
* Class: sun_security_ec_ECDSASignature
* Method: verifySignedDigest
* Signature: ([B[B[B[B)Z
*/
JNIEXPORT jboolean
JNICALL Java_sun_security_ec_ECDSASignature_verifySignedDigest
(JNIEnv *env, jclass clazz, jbyteArray signedDigest, jbyteArray digest, jbyteArray publicKey, jbyteArray encodedParams)
{
jboolean isValid = false;
// Copy signedDigest from Java to native buffer
jbyte* pSignedDigestBuffer = NULL;
jint jSignedDigestLength = env->GetArrayLength(signedDigest);
pSignedDigestBuffer = new jbyte[jSignedDigestLength];
env->GetByteArrayRegion(signedDigest, 0, jSignedDigestLength,
pSignedDigestBuffer);
SECItem signature_item;
signature_item.data = (unsigned char *) pSignedDigestBuffer;
signature_item.len = jSignedDigestLength;
// Copy digest from Java to native buffer
jbyte* pDigestBuffer = NULL;
jint jDigestLength = env->GetArrayLength(digest);
pDigestBuffer = new jbyte[jDigestLength];
env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
SECItem digest_item;
digest_item.data = (unsigned char *) pDigestBuffer;
digest_item.len = jDigestLength;
// Extract public key data
ECPublicKey pubKey;
pubKey.publicValue.data = NULL;
ECParams *ecparams = NULL;
SECKEYECParams params_item;
// Initialize the ECParams struct
params_item.len = env->GetArrayLength(encodedParams);
params_item.data =
(unsigned char *) env->GetByteArrayElements(encodedParams, 0);
// Fill a new ECParams using the supplied OID
if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) {
/* bad curve OID */
ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
goto cleanup;
}
pubKey.ecParams = *ecparams; // struct assignment
pubKey.publicValue.len = env->GetArrayLength(publicKey);
pubKey.publicValue.data =
(unsigned char *) env->GetByteArrayElements(publicKey, 0);
if (ECDSA_VerifyDigest(&pubKey, &signature_item, &digest_item, 0)
!= SECSuccess) {
goto cleanup;
}
isValid = true;
cleanup:
{
if (params_item.data)
env->ReleaseByteArrayElements(encodedParams,
(jbyte *) params_item.data, JNI_ABORT);
if (pubKey.publicValue.data)
env->ReleaseByteArrayElements(publicKey,
(jbyte *) pubKey.publicValue.data, JNI_ABORT);
if (ecparams)
FreeECParams(ecparams, true);
if (pSignedDigestBuffer)
delete [] pSignedDigestBuffer;
if (pDigestBuffer)
delete [] pDigestBuffer;
}
return isValid;
}
/*
* Class: sun_security_ec_ECDHKeyAgreement
* Method: deriveKey
* Signature: ([B[B[B)[B
*/
JNIEXPORT jbyteArray
JNICALL Java_sun_security_ec_ECDHKeyAgreement_deriveKey
(JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey, jbyteArray encodedParams)
{
jbyteArray jSecret = NULL;
// Extract private key value
SECItem privateValue_item;
privateValue_item.len = env->GetArrayLength(privateKey);
privateValue_item.data =
(unsigned char *) env->GetByteArrayElements(privateKey, 0);
// Extract public key value
SECItem publicValue_item;
publicValue_item.len = env->GetArrayLength(publicKey);
publicValue_item.data =
(unsigned char *) env->GetByteArrayElements(publicKey, 0);
// Initialize the ECParams struct
ECParams *ecparams = NULL;
SECKEYECParams params_item;
params_item.len = env->GetArrayLength(encodedParams);
params_item.data =
(unsigned char *) env->GetByteArrayElements(encodedParams, 0);
// Fill a new ECParams using the supplied OID
if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) {
/* bad curve OID */
ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
goto cleanup;
}
// Prepare a buffer for the secret
SECItem secret_item;
secret_item.data = NULL;
secret_item.len = ecparams->order.len * 2;
if (ECDH_Derive(&publicValue_item, ecparams, &privateValue_item, B_FALSE,
&secret_item, 0) != SECSuccess) {
ThrowException(env, ILLEGAL_STATE_EXCEPTION);
goto cleanup;
}
// Create new byte array
jSecret = env->NewByteArray(secret_item.len);
// Copy bytes from the SECItem buffer to a Java byte array
env->SetByteArrayRegion(jSecret, 0, secret_item.len,
(jbyte *)secret_item.data);
// Free the SECItem data buffer
SECITEM_FreeItem(&secret_item, B_FALSE);
cleanup:
{
if (privateValue_item.data)
env->ReleaseByteArrayElements(privateKey,
(jbyte *) privateValue_item.data, JNI_ABORT);
if (publicValue_item.data)
env->ReleaseByteArrayElements(publicKey,
(jbyte *) publicValue_item.data, JNI_ABORT);
if (params_item.data)
env->ReleaseByteArrayElements(encodedParams,
(jbyte *) params_item.data, JNI_ABORT);
if (ecparams)
FreeECParams(ecparams, true);
}
return jSecret;
}
} /* extern "C" */