1 /*
   2  * Copyright (c) 2009, 2017, 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 "jni_util.h"
  28 #include "impl/ecc_impl.h"
  29 
  30 #define ILLEGAL_STATE_EXCEPTION "java/lang/IllegalStateException"
  31 #define INVALID_ALGORITHM_PARAMETER_EXCEPTION \
  32         "java/security/InvalidAlgorithmParameterException"
  33 #define INVALID_PARAMETER_EXCEPTION \
  34         "java/security/InvalidParameterException"
  35 #define KEY_EXCEPTION   "java/security/KeyException"
  36 
  37 extern "C" {
  38 
  39 /*
  40  * Declare library specific JNI_Onload entry if static build
  41  */
  42 DEF_STATIC_JNI_OnLoad
  43 
  44 /*
  45  * Throws an arbitrary Java exception.
  46  */
  47 void ThrowException(JNIEnv *env, const char *exceptionName)
  48 {
  49     jclass exceptionClazz = env->FindClass(exceptionName);
  50     if (exceptionClazz != NULL) {
  51         env->ThrowNew(exceptionClazz, NULL);
  52     }
  53 }
  54 
  55 /*
  56  * Deep free of the ECParams struct
  57  */
  58 void FreeECParams(ECParams *ecparams, jboolean freeStruct)
  59 {
  60     // Use B_FALSE to free the SECItem->data element, but not the SECItem itself
  61     // Use B_TRUE to free both
  62 
  63     SECITEM_FreeItem(&ecparams->fieldID.u.prime, B_FALSE);
  64     SECITEM_FreeItem(&ecparams->curve.a, B_FALSE);
  65     SECITEM_FreeItem(&ecparams->curve.b, B_FALSE);
  66     SECITEM_FreeItem(&ecparams->curve.seed, B_FALSE);
  67     SECITEM_FreeItem(&ecparams->base, B_FALSE);
  68     SECITEM_FreeItem(&ecparams->order, B_FALSE);
  69     SECITEM_FreeItem(&ecparams->DEREncoding, B_FALSE);
  70     SECITEM_FreeItem(&ecparams->curveOID, B_FALSE);
  71     if (freeStruct)
  72         free(ecparams);
  73 }
  74 
  75 jbyteArray getEncodedBytes(JNIEnv *env, SECItem *hSECItem)
  76 {
  77     SECItem *s = (SECItem *)hSECItem;
  78 
  79     jbyteArray jEncodedBytes = env->NewByteArray(s->len);
  80     if (jEncodedBytes == NULL) {
  81         return NULL;
  82     }
  83     // Copy bytes from a native SECItem buffer to Java byte array
  84     env->SetByteArrayRegion(jEncodedBytes, 0, s->len, (jbyte *)s->data);
  85     if (env->ExceptionCheck()) { // should never happen
  86         return NULL;
  87     }
  88     return jEncodedBytes;
  89 }
  90 
  91 /*
  92  * Class:     sun_security_ec_ECKeyPairGenerator
  93  * Method:    isCurveSupported
  94  * Signature: ([B)Z
  95  */
  96 JNIEXPORT jboolean
  97 JNICALL Java_sun_security_ec_ECKeyPairGenerator_isCurveSupported
  98   (JNIEnv *env, jclass clazz, jbyteArray encodedParams)
  99 {
 100     SECKEYECParams params_item;
 101     ECParams *ecparams = NULL;
 102     jboolean result = JNI_FALSE;
 103 
 104     // The curve is supported if we can get parameters for it
 105     params_item.len = env->GetArrayLength(encodedParams);
 106     params_item.data =
 107         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 108     if (params_item.data == NULL) {
 109         goto cleanup;
 110     }
 111 
 112     // Fill a new ECParams using the supplied OID
 113     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 114         /* bad curve OID */
 115         goto cleanup;
 116     }
 117 
 118     // If we make it to here, then the curve is supported
 119     result = JNI_TRUE;
 120 
 121 cleanup:
 122     {
 123         if (params_item.data) {
 124             env->ReleaseByteArrayElements(encodedParams,
 125                 (jbyte *) params_item.data, JNI_ABORT);
 126         }
 127         if (ecparams) {
 128             FreeECParams(ecparams, true);
 129         }
 130     }
 131 
 132     return result;
 133 }
 134 
 135 /*
 136  * Class:     sun_security_ec_ECKeyPairGenerator
 137  * Method:    generateECKeyPair
 138  * Signature: (I[B[B)[[B
 139  */
 140 JNIEXPORT jobjectArray
 141 JNICALL Java_sun_security_ec_ECKeyPairGenerator_generateECKeyPair
 142   (JNIEnv *env, jclass clazz, jint keySize, jbyteArray encodedParams, jbyteArray seed)
 143 {
 144     ECPrivateKey *privKey = NULL; // contains both public and private values
 145     ECParams *ecparams = NULL;
 146     SECKEYECParams params_item;
 147     jint jSeedLength;
 148     jbyte* pSeedBuffer = NULL;
 149     jobjectArray result = NULL;
 150     jclass baCls = NULL;
 151     jbyteArray jba;
 152 
 153     // Initialize the ECParams struct
 154     params_item.len = env->GetArrayLength(encodedParams);
 155     params_item.data =
 156         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 157     if (params_item.data == NULL) {
 158         goto cleanup;
 159     }
 160 
 161     // Fill a new ECParams using the supplied OID
 162     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 163         /* bad curve OID */
 164         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 165         goto cleanup;
 166     }
 167 
 168     // Copy seed from Java to native buffer
 169     jSeedLength = env->GetArrayLength(seed);
 170     pSeedBuffer = new jbyte[jSeedLength];
 171     env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
 172 
 173     // Generate the new keypair (using the supplied seed)
 174     if (EC_NewKey(ecparams, &privKey, (unsigned char *) pSeedBuffer,
 175         jSeedLength, 0) != SECSuccess) {
 176         ThrowException(env, KEY_EXCEPTION);
 177         goto cleanup;
 178     }
 179 
 180     jboolean isCopy;
 181     baCls = env->FindClass("[B");
 182     if (baCls == NULL) {
 183         goto cleanup;
 184     }
 185     result = env->NewObjectArray(2, baCls, NULL);
 186     if (result == NULL) {
 187         goto cleanup;
 188     }
 189     jba = getEncodedBytes(env, &(privKey->privateValue));
 190     if (jba == NULL) {
 191         result = NULL;
 192         goto cleanup;
 193     }
 194     env->SetObjectArrayElement(result, 0, jba); // big integer
 195     if (env->ExceptionCheck()) { // should never happen
 196         result = NULL;
 197         goto cleanup;
 198     }
 199 
 200     jba = getEncodedBytes(env, &(privKey->publicValue));
 201     if (jba == NULL) {
 202         result = NULL;
 203         goto cleanup;
 204     }
 205     env->SetObjectArrayElement(result, 1, jba); // encoded ec point
 206     if (env->ExceptionCheck()) { // should never happen
 207         result = NULL;
 208         goto cleanup;
 209     }
 210 
 211 cleanup:
 212     {
 213         if (params_item.data) {
 214             env->ReleaseByteArrayElements(encodedParams,
 215                 (jbyte *) params_item.data, JNI_ABORT);
 216         }
 217         if (ecparams) {
 218             FreeECParams(ecparams, true);
 219         }
 220         if (privKey) {
 221             FreeECParams(&privKey->ecParams, false);
 222             SECITEM_FreeItem(&privKey->version, B_FALSE);
 223             SECITEM_FreeItem(&privKey->privateValue, B_FALSE);
 224             SECITEM_FreeItem(&privKey->publicValue, B_FALSE);
 225             free(privKey);
 226         }
 227 
 228         if (pSeedBuffer) {
 229             delete [] pSeedBuffer;
 230         }
 231     }
 232 
 233     return result;
 234 }
 235 
 236 /*
 237  * Class:     sun_security_ec_ECDSASignature
 238  * Method:    signDigest
 239  * Signature: ([B[B[B[B)[B
 240  */
 241 JNIEXPORT jbyteArray
 242 JNICALL Java_sun_security_ec_ECDSASignature_signDigest
 243   (JNIEnv *env, jclass clazz, jbyteArray digest, jbyteArray privateKey, jbyteArray encodedParams, jbyteArray seed, jint timing)
 244 {
 245     jbyte* pDigestBuffer = NULL;
 246     jint jDigestLength = env->GetArrayLength(digest);
 247     jbyteArray jSignedDigest = NULL;
 248 
 249     SECItem signature_item;
 250     jbyte* pSignedDigestBuffer = NULL;
 251     jbyteArray temp;
 252 
 253     jint jSeedLength = env->GetArrayLength(seed);
 254     jbyte* pSeedBuffer = NULL;
 255 
 256     // Copy digest from Java to native buffer
 257     pDigestBuffer = new jbyte[jDigestLength];
 258     env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
 259     SECItem digest_item;
 260     digest_item.data = (unsigned char *) pDigestBuffer;
 261     digest_item.len = jDigestLength;
 262 
 263     ECPrivateKey privKey;
 264     privKey.privateValue.data = NULL;
 265 
 266     // Initialize the ECParams struct
 267     ECParams *ecparams = NULL;
 268     SECKEYECParams params_item;
 269     params_item.len = env->GetArrayLength(encodedParams);
 270     params_item.data =
 271         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 272     if (params_item.data == NULL) {
 273         goto cleanup;
 274     }
 275 
 276     // Fill a new ECParams using the supplied OID
 277     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 278         /* bad curve OID */
 279         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 280         goto cleanup;
 281     }
 282 
 283     // Extract private key data
 284     privKey.ecParams = *ecparams; // struct assignment
 285     privKey.privateValue.len = env->GetArrayLength(privateKey);
 286     privKey.privateValue.data =
 287         (unsigned char *) env->GetByteArrayElements(privateKey, 0);
 288     if (privKey.privateValue.data == NULL) {
 289         goto cleanup;
 290     }
 291 
 292     // Prepare a buffer for the signature (twice the key length)
 293     pSignedDigestBuffer = new jbyte[ecparams->order.len * 2];
 294     signature_item.data = (unsigned char *) pSignedDigestBuffer;
 295     signature_item.len = ecparams->order.len * 2;
 296 
 297     // Copy seed from Java to native buffer
 298     pSeedBuffer = new jbyte[jSeedLength];
 299     env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
 300 
 301     // Sign the digest (using the supplied seed)
 302     if (ECDSA_SignDigest(&privKey, &signature_item, &digest_item,
 303         (unsigned char *) pSeedBuffer, jSeedLength, 0, timing) != SECSuccess) {
 304         ThrowException(env, KEY_EXCEPTION);
 305         goto cleanup;
 306     }
 307 
 308     // Create new byte array
 309     temp = env->NewByteArray(signature_item.len);
 310     if (temp == NULL) {
 311         goto cleanup;
 312     }
 313 
 314     // Copy data from native buffer
 315     env->SetByteArrayRegion(temp, 0, signature_item.len, pSignedDigestBuffer);
 316     jSignedDigest = temp;
 317 
 318 cleanup:
 319     {
 320         if (params_item.data) {
 321             env->ReleaseByteArrayElements(encodedParams,
 322                 (jbyte *) params_item.data, JNI_ABORT);
 323         }
 324         if (privKey.privateValue.data) {
 325             env->ReleaseByteArrayElements(privateKey,
 326                 (jbyte *) privKey.privateValue.data, JNI_ABORT);
 327         }
 328         if (pDigestBuffer) {
 329             delete [] pDigestBuffer;
 330         }
 331         if (pSignedDigestBuffer) {
 332             delete [] pSignedDigestBuffer;
 333         }
 334         if (pSeedBuffer) {
 335             delete [] pSeedBuffer;
 336         }
 337         if (ecparams) {
 338             FreeECParams(ecparams, true);
 339         }
 340     }
 341 
 342     return jSignedDigest;
 343 }
 344 
 345 /*
 346  * Class:     sun_security_ec_ECDSASignature
 347  * Method:    verifySignedDigest
 348  * Signature: ([B[B[B[B)Z
 349  */
 350 JNIEXPORT jboolean
 351 JNICALL Java_sun_security_ec_ECDSASignature_verifySignedDigest
 352   (JNIEnv *env, jclass clazz, jbyteArray signedDigest, jbyteArray digest, jbyteArray publicKey, jbyteArray encodedParams)
 353 {
 354     jboolean isValid = false;
 355 
 356     // Copy signedDigest from Java to native buffer
 357     jbyte* pSignedDigestBuffer = NULL;
 358     jint jSignedDigestLength = env->GetArrayLength(signedDigest);
 359     pSignedDigestBuffer = new jbyte[jSignedDigestLength];
 360     env->GetByteArrayRegion(signedDigest, 0, jSignedDigestLength,
 361         pSignedDigestBuffer);
 362     SECItem signature_item;
 363     signature_item.data = (unsigned char *) pSignedDigestBuffer;
 364     signature_item.len = jSignedDigestLength;
 365 
 366     // Copy digest from Java to native buffer
 367     jbyte* pDigestBuffer = NULL;
 368     jint jDigestLength = env->GetArrayLength(digest);
 369     pDigestBuffer = new jbyte[jDigestLength];
 370     env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
 371     SECItem digest_item;
 372     digest_item.data = (unsigned char *) pDigestBuffer;
 373     digest_item.len = jDigestLength;
 374 
 375     // Extract public key data
 376     ECPublicKey pubKey;
 377     pubKey.publicValue.data = NULL;
 378     ECParams *ecparams = NULL;
 379     SECKEYECParams params_item;
 380 
 381     // Initialize the ECParams struct
 382     params_item.len = env->GetArrayLength(encodedParams);
 383     params_item.data =
 384         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 385     if (params_item.data == NULL) {
 386         goto cleanup;
 387     }
 388 
 389     // Fill a new ECParams using the supplied OID
 390     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 391         /* bad curve OID */
 392         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 393         goto cleanup;
 394     }
 395     pubKey.ecParams = *ecparams; // struct assignment
 396     pubKey.publicValue.len = env->GetArrayLength(publicKey);
 397     pubKey.publicValue.data =
 398         (unsigned char *) env->GetByteArrayElements(publicKey, 0);
 399 
 400     if (ECDSA_VerifyDigest(&pubKey, &signature_item, &digest_item, 0)
 401             != SECSuccess) {
 402         goto cleanup;
 403     }
 404 
 405     isValid = true;
 406 
 407 cleanup:
 408     {
 409         if (params_item.data)
 410             env->ReleaseByteArrayElements(encodedParams,
 411                 (jbyte *) params_item.data, JNI_ABORT);
 412 
 413         if (pubKey.publicValue.data)
 414             env->ReleaseByteArrayElements(publicKey,
 415                 (jbyte *) pubKey.publicValue.data, JNI_ABORT);
 416 
 417         if (ecparams)
 418             FreeECParams(ecparams, true);
 419 
 420         if (pSignedDigestBuffer)
 421             delete [] pSignedDigestBuffer;
 422 
 423         if (pDigestBuffer)
 424             delete [] pDigestBuffer;
 425     }
 426 
 427     return isValid;
 428 }
 429 
 430 /*
 431  * Class:     sun_security_ec_ECDHKeyAgreement
 432  * Method:    deriveKey
 433  * Signature: ([B[B[B)[B
 434  */
 435 JNIEXPORT jbyteArray
 436 JNICALL Java_sun_security_ec_ECDHKeyAgreement_deriveKey
 437   (JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey, jbyteArray encodedParams)
 438 {
 439     jbyteArray jSecret = NULL;
 440     ECParams *ecparams = NULL;
 441     SECItem privateValue_item;
 442     privateValue_item.data = NULL;
 443     SECItem publicValue_item;
 444     publicValue_item.data = NULL;
 445     SECKEYECParams params_item;
 446     params_item.data = NULL;
 447 
 448     // Extract private key value
 449     privateValue_item.len = env->GetArrayLength(privateKey);
 450     privateValue_item.data =
 451             (unsigned char *) env->GetByteArrayElements(privateKey, 0);
 452     if (privateValue_item.data == NULL) {
 453         goto cleanup;
 454     }
 455 
 456     // Extract public key value
 457     publicValue_item.len = env->GetArrayLength(publicKey);
 458     publicValue_item.data =
 459         (unsigned char *) env->GetByteArrayElements(publicKey, 0);
 460     if (publicValue_item.data == NULL) {
 461         goto cleanup;
 462     }
 463 
 464     // Initialize the ECParams struct
 465     params_item.len = env->GetArrayLength(encodedParams);
 466     params_item.data =
 467         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 468     if (params_item.data == NULL) {
 469         goto cleanup;
 470     }
 471 
 472     // Fill a new ECParams using the supplied OID
 473     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 474         /* bad curve OID */
 475         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 476         goto cleanup;
 477     }
 478 
 479     // Prepare a buffer for the secret
 480     SECItem secret_item;
 481     secret_item.data = NULL;
 482     secret_item.len = ecparams->order.len * 2;
 483 
 484     if (ECDH_Derive(&publicValue_item, ecparams, &privateValue_item, B_FALSE,
 485         &secret_item, 0) != SECSuccess) {
 486         ThrowException(env, ILLEGAL_STATE_EXCEPTION);
 487         goto cleanup;
 488     }
 489 
 490     // Create new byte array
 491     jSecret = env->NewByteArray(secret_item.len);
 492     if (jSecret == NULL) {
 493         goto cleanup;
 494     }
 495 
 496     // Copy bytes from the SECItem buffer to a Java byte array
 497     env->SetByteArrayRegion(jSecret, 0, secret_item.len,
 498         (jbyte *)secret_item.data);
 499 
 500     // Free the SECItem data buffer
 501     SECITEM_FreeItem(&secret_item, B_FALSE);
 502 
 503 cleanup:
 504     {
 505         if (privateValue_item.data)
 506             env->ReleaseByteArrayElements(privateKey,
 507                 (jbyte *) privateValue_item.data, JNI_ABORT);
 508 
 509         if (publicValue_item.data)
 510             env->ReleaseByteArrayElements(publicKey,
 511                 (jbyte *) publicValue_item.data, JNI_ABORT);
 512 
 513         if (params_item.data)
 514             env->ReleaseByteArrayElements(encodedParams,
 515                 (jbyte *) params_item.data, JNI_ABORT);
 516 
 517         if (ecparams)
 518             FreeECParams(ecparams, true);
 519     }
 520 
 521     return jSecret;
 522 }
 523 
 524 } /* extern "C" */