1 /*
   2  * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <jni.h>
  27 #include "impl/ecc_impl.h"
  28 
  29 #define ILLEGAL_STATE_EXCEPTION "java/lang/IllegalStateException"
  30 #define INVALID_ALGORITHM_PARAMETER_EXCEPTION \
  31         "java/security/InvalidAlgorithmParameterException"
  32 #define INVALID_PARAMETER_EXCEPTION \
  33         "java/security/InvalidParameterException"
  34 #define KEY_EXCEPTION   "java/security/KeyException"
  35 
  36 extern "C" {
  37 
  38 /*
  39  * Throws an arbitrary Java exception.
  40  */
  41 void ThrowException(JNIEnv *env, const char *exceptionName)
  42 {
  43     jclass exceptionClazz = env->FindClass(exceptionName);
  44     if (exceptionClazz != NULL) {
  45         env->ThrowNew(exceptionClazz, NULL);
  46     }
  47 }
  48 
  49 /*
  50  * Deep free of the ECParams struct
  51  */
  52 void FreeECParams(ECParams *ecparams, jboolean freeStruct)
  53 {
  54     // Use B_FALSE to free the SECItem->data element, but not the SECItem itself
  55     // Use B_TRUE to free both
  56 
  57     SECITEM_FreeItem(&ecparams->fieldID.u.prime, B_FALSE);
  58     SECITEM_FreeItem(&ecparams->curve.a, B_FALSE);
  59     SECITEM_FreeItem(&ecparams->curve.b, B_FALSE);
  60     SECITEM_FreeItem(&ecparams->curve.seed, B_FALSE);
  61     SECITEM_FreeItem(&ecparams->base, B_FALSE);
  62     SECITEM_FreeItem(&ecparams->order, B_FALSE);
  63     SECITEM_FreeItem(&ecparams->DEREncoding, B_FALSE);
  64     SECITEM_FreeItem(&ecparams->curveOID, B_FALSE);
  65     if (freeStruct)
  66         free(ecparams);
  67 }
  68 
  69 /*
  70  * Class:     sun_security_ec_ECKeyPairGenerator
  71  * Method:    generateECKeyPair
  72  * Signature: (I[B[B)[J
  73  */
  74 JNIEXPORT jlongArray
  75 JNICALL Java_sun_security_ec_ECKeyPairGenerator_generateECKeyPair
  76   (JNIEnv *env, jclass clazz, jint keySize, jbyteArray encodedParams, jbyteArray seed)
  77 {
  78     ECPrivateKey *privKey = NULL; /* contains both public and private values */
  79     ECParams *ecparams = NULL;
  80     SECKEYECParams params_item;
  81     jint jSeedLength;
  82     jbyte* pSeedBuffer = NULL;
  83     jlongArray result = NULL;
  84     jlong* resultElements = NULL;
  85 
  86     // Initialize the ECParams struct
  87     params_item.len = env->GetArrayLength(encodedParams);
  88     params_item.data =
  89         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
  90     if (params_item.data == NULL) {
  91         goto cleanup;
  92     }
  93 
  94     // Fill a new ECParams using the supplied OID
  95     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
  96         /* bad curve OID */
  97         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
  98         goto cleanup;
  99     }
 100 
 101     // Copy seed from Java to native buffer
 102     jSeedLength = env->GetArrayLength(seed);
 103     pSeedBuffer = new jbyte[jSeedLength];
 104     env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
 105 
 106     // Generate the new keypair (using the supplied seed)
 107     if (EC_NewKey(ecparams, &privKey, (unsigned char *) pSeedBuffer,
 108         jSeedLength, 0) != SECSuccess) {
 109         ThrowException(env, KEY_EXCEPTION);
 110         goto cleanup;
 111     }
 112 
 113     jboolean isCopy;
 114     result = env->NewLongArray(2);
 115     if (result == NULL) {
 116         goto cleanup;
 117     }
 118 
 119     resultElements = env->GetLongArrayElements(result, &isCopy);
 120     if (resultElements == NULL) {
 121         goto cleanup;
 122     }
 123 
 124     resultElements[0] = (jlong) &(privKey->privateValue); // private big integer
 125     resultElements[1] = (jlong) &(privKey->publicValue); // encoded ec point
 126 
 127     // If the array is a copy then we must write back our changes
 128     if (isCopy == JNI_TRUE) {
 129         env->ReleaseLongArrayElements(result, resultElements, 0);
 130     }
 131 
 132 cleanup:
 133     {
 134         if (params_item.data)
 135             env->ReleaseByteArrayElements(encodedParams,
 136                 (jbyte *) params_item.data, JNI_ABORT);
 137 
 138         if (ecparams)
 139             FreeECParams(ecparams, true);
 140 
 141         if (privKey) {
 142             FreeECParams(&privKey->ecParams, false);
 143             SECITEM_FreeItem(&privKey->version, B_FALSE);
 144             // Don't free privKey->privateValue and privKey->publicValue
 145         }
 146 
 147         if (pSeedBuffer)
 148             delete [] pSeedBuffer;
 149     }
 150 
 151     return result;
 152 }
 153 
 154 /*
 155  * Class:     sun_security_ec_ECKeyPairGenerator
 156  * Method:    getEncodedBytes
 157  * Signature: (J)[B
 158  */
 159 JNIEXPORT jbyteArray
 160 JNICALL Java_sun_security_ec_ECKeyPairGenerator_getEncodedBytes
 161   (JNIEnv *env, jclass clazz, jlong hSECItem)
 162 {
 163     SECItem *s = (SECItem *)hSECItem;
 164     jbyteArray jEncodedBytes = env->NewByteArray(s->len);
 165     if (jEncodedBytes == NULL) {
 166         return NULL;
 167     }
 168 
 169     // Copy bytes from a native SECItem buffer to Java byte array
 170     env->SetByteArrayRegion(jEncodedBytes, 0, s->len, (jbyte *)s->data);
 171 
 172     // Use B_FALSE to free only the SECItem->data
 173     SECITEM_FreeItem(s, B_FALSE);
 174 
 175     return jEncodedBytes;
 176 }
 177 
 178 /*
 179  * Class:     sun_security_ec_ECDSASignature
 180  * Method:    signDigest
 181  * Signature: ([B[B[B[B)[B
 182  */
 183 JNIEXPORT jbyteArray
 184 JNICALL Java_sun_security_ec_ECDSASignature_signDigest
 185   (JNIEnv *env, jclass clazz, jbyteArray digest, jbyteArray privateKey, jbyteArray encodedParams, jbyteArray seed)
 186 {
 187     jbyte* pDigestBuffer = NULL;
 188     jint jDigestLength = env->GetArrayLength(digest);
 189     jbyteArray jSignedDigest = NULL;
 190 
 191     SECItem signature_item;
 192     jbyte* pSignedDigestBuffer = NULL;
 193     jbyteArray temp;
 194 
 195     jint jSeedLength = env->GetArrayLength(seed);
 196     jbyte* pSeedBuffer = NULL;
 197 
 198     // Copy digest from Java to native buffer
 199     pDigestBuffer = new jbyte[jDigestLength];
 200     env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
 201     SECItem digest_item;
 202     digest_item.data = (unsigned char *) pDigestBuffer;
 203     digest_item.len = jDigestLength;
 204 
 205     ECPrivateKey privKey;
 206 
 207     // Initialize the ECParams struct
 208     ECParams *ecparams = NULL;
 209     SECKEYECParams params_item;
 210     params_item.len = env->GetArrayLength(encodedParams);
 211     params_item.data =
 212         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 213     if (params_item.data == NULL) {
 214         goto cleanup;
 215     }
 216 
 217     // Fill a new ECParams using the supplied OID
 218     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 219         /* bad curve OID */
 220         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 221         goto cleanup;
 222     }
 223 
 224     // Extract private key data
 225     privKey.ecParams = *ecparams; // struct assignment
 226     privKey.privateValue.len = env->GetArrayLength(privateKey);
 227     privKey.privateValue.data =
 228         (unsigned char *) env->GetByteArrayElements(privateKey, 0);
 229     if (privKey.privateValue.data == NULL) {
 230         goto cleanup;
 231     }
 232 
 233     // Prepare a buffer for the signature (twice the key length)
 234     pSignedDigestBuffer = new jbyte[ecparams->order.len * 2];
 235     signature_item.data = (unsigned char *) pSignedDigestBuffer;
 236     signature_item.len = ecparams->order.len * 2;
 237 
 238     // Copy seed from Java to native buffer
 239     pSeedBuffer = new jbyte[jSeedLength];
 240     env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
 241 
 242     // Sign the digest (using the supplied seed)
 243     if (ECDSA_SignDigest(&privKey, &signature_item, &digest_item,
 244         (unsigned char *) pSeedBuffer, jSeedLength, 0) != SECSuccess) {
 245         ThrowException(env, KEY_EXCEPTION);
 246         goto cleanup;
 247     }
 248 
 249     // Create new byte array
 250     temp = env->NewByteArray(signature_item.len);
 251     if (temp == NULL) {
 252         goto cleanup;
 253     }
 254 
 255     // Copy data from native buffer
 256     env->SetByteArrayRegion(temp, 0, signature_item.len, pSignedDigestBuffer);
 257     jSignedDigest = temp;
 258 
 259 cleanup:
 260     {
 261         if (params_item.data)
 262             env->ReleaseByteArrayElements(encodedParams,
 263                 (jbyte *) params_item.data, JNI_ABORT);
 264 
 265         if (pDigestBuffer)
 266             delete [] pDigestBuffer;
 267 
 268         if (pSignedDigestBuffer)
 269             delete [] pSignedDigestBuffer;
 270 
 271         if (pSeedBuffer)
 272             delete [] pSeedBuffer;
 273 
 274         if (ecparams)
 275             FreeECParams(ecparams, true);
 276     }
 277 
 278     return jSignedDigest;
 279 }
 280 
 281 /*
 282  * Class:     sun_security_ec_ECDSASignature
 283  * Method:    verifySignedDigest
 284  * Signature: ([B[B[B[B)Z
 285  */
 286 JNIEXPORT jboolean
 287 JNICALL Java_sun_security_ec_ECDSASignature_verifySignedDigest
 288   (JNIEnv *env, jclass clazz, jbyteArray signedDigest, jbyteArray digest, jbyteArray publicKey, jbyteArray encodedParams)
 289 {
 290     jboolean isValid = false;
 291 
 292     // Copy signedDigest from Java to native buffer
 293     jbyte* pSignedDigestBuffer = NULL;
 294     jint jSignedDigestLength = env->GetArrayLength(signedDigest);
 295     pSignedDigestBuffer = new jbyte[jSignedDigestLength];
 296     env->GetByteArrayRegion(signedDigest, 0, jSignedDigestLength,
 297         pSignedDigestBuffer);
 298     SECItem signature_item;
 299     signature_item.data = (unsigned char *) pSignedDigestBuffer;
 300     signature_item.len = jSignedDigestLength;
 301 
 302     // Copy digest from Java to native buffer
 303     jbyte* pDigestBuffer = NULL;
 304     jint jDigestLength = env->GetArrayLength(digest);
 305     pDigestBuffer = new jbyte[jDigestLength];
 306     env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
 307     SECItem digest_item;
 308     digest_item.data = (unsigned char *) pDigestBuffer;
 309     digest_item.len = jDigestLength;
 310 
 311     // Extract public key data
 312     ECPublicKey pubKey;
 313     pubKey.publicValue.data = NULL;
 314     ECParams *ecparams = NULL;
 315     SECKEYECParams params_item;
 316 
 317     // Initialize the ECParams struct
 318     params_item.len = env->GetArrayLength(encodedParams);
 319     params_item.data =
 320         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 321     if (params_item.data == NULL) {
 322         goto cleanup;
 323     }
 324 
 325     // Fill a new ECParams using the supplied OID
 326     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 327         /* bad curve OID */
 328         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 329         goto cleanup;
 330     }
 331     pubKey.ecParams = *ecparams; // struct assignment
 332     pubKey.publicValue.len = env->GetArrayLength(publicKey);
 333     pubKey.publicValue.data =
 334         (unsigned char *) env->GetByteArrayElements(publicKey, 0);
 335 
 336     if (ECDSA_VerifyDigest(&pubKey, &signature_item, &digest_item, 0)
 337             != SECSuccess) {
 338         goto cleanup;
 339     }
 340 
 341     isValid = true;
 342 
 343 cleanup:
 344     {
 345         if (params_item.data)
 346             env->ReleaseByteArrayElements(encodedParams,
 347                 (jbyte *) params_item.data, JNI_ABORT);
 348 
 349         if (pubKey.publicValue.data)
 350             env->ReleaseByteArrayElements(publicKey,
 351                 (jbyte *) pubKey.publicValue.data, JNI_ABORT);
 352 
 353         if (ecparams)
 354             FreeECParams(ecparams, true);
 355 
 356         if (pSignedDigestBuffer)
 357             delete [] pSignedDigestBuffer;
 358 
 359         if (pDigestBuffer)
 360             delete [] pDigestBuffer;
 361     }
 362 
 363     return isValid;
 364 }
 365 
 366 /*
 367  * Class:     sun_security_ec_ECDHKeyAgreement
 368  * Method:    deriveKey
 369  * Signature: ([B[B[B)[B
 370  */
 371 JNIEXPORT jbyteArray
 372 JNICALL Java_sun_security_ec_ECDHKeyAgreement_deriveKey
 373   (JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey, jbyteArray encodedParams)
 374 {
 375     jbyteArray jSecret = NULL;
 376     ECParams *ecparams = NULL;
 377 
 378     // Extract private key value
 379     SECItem privateValue_item;
 380     privateValue_item.len = env->GetArrayLength(privateKey);
 381     privateValue_item.data =
 382             (unsigned char *) env->GetByteArrayElements(privateKey, 0);
 383     if (privateValue_item.data == NULL) {
 384         goto cleanup;
 385     }
 386 
 387     // Extract public key value
 388     SECItem publicValue_item;
 389     publicValue_item.len = env->GetArrayLength(publicKey);
 390     publicValue_item.data =
 391         (unsigned char *) env->GetByteArrayElements(publicKey, 0);
 392     if (publicValue_item.data == NULL) {
 393         goto cleanup;
 394     }
 395 
 396     // Initialize the ECParams struct
 397     SECKEYECParams params_item;
 398     params_item.len = env->GetArrayLength(encodedParams);
 399     params_item.data =
 400         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 401     if (params_item.data == NULL) {
 402         goto cleanup;
 403     }
 404 
 405     // Fill a new ECParams using the supplied OID
 406     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 407         /* bad curve OID */
 408         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 409         goto cleanup;
 410     }
 411 
 412     // Prepare a buffer for the secret
 413     SECItem secret_item;
 414     secret_item.data = NULL;
 415     secret_item.len = ecparams->order.len * 2;
 416 
 417     if (ECDH_Derive(&publicValue_item, ecparams, &privateValue_item, B_FALSE,
 418         &secret_item, 0) != SECSuccess) {
 419         ThrowException(env, ILLEGAL_STATE_EXCEPTION);
 420         goto cleanup;
 421     }
 422 
 423     // Create new byte array
 424     jSecret = env->NewByteArray(secret_item.len);
 425     if (jSecret == NULL) {
 426         goto cleanup;
 427     }
 428 
 429     // Copy bytes from the SECItem buffer to a Java byte array
 430     env->SetByteArrayRegion(jSecret, 0, secret_item.len,
 431         (jbyte *)secret_item.data);
 432 
 433     // Free the SECItem data buffer
 434     SECITEM_FreeItem(&secret_item, B_FALSE);
 435 
 436 cleanup:
 437     {
 438         if (privateValue_item.data)
 439             env->ReleaseByteArrayElements(privateKey,
 440                 (jbyte *) privateValue_item.data, JNI_ABORT);
 441 
 442         if (publicValue_item.data)
 443             env->ReleaseByteArrayElements(publicKey,
 444                 (jbyte *) publicValue_item.data, JNI_ABORT);
 445 
 446         if (params_item.data)
 447             env->ReleaseByteArrayElements(encodedParams,
 448                 (jbyte *) params_item.data, JNI_ABORT);
 449 
 450         if (ecparams)
 451             FreeECParams(ecparams, true);
 452     }
 453 
 454     return jSecret;
 455 }
 456 
 457 } /* extern "C" */