1 /*
   2  * Copyright (c) 2009, 2015, 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:    generateECKeyPair
  94  * Signature: (I[B[B)[[B
  95  */
  96 JNIEXPORT jobjectArray
  97 JNICALL Java_sun_security_ec_ECKeyPairGenerator_generateECKeyPair
  98   (JNIEnv *env, jclass clazz, jint keySize, jbyteArray encodedParams, jbyteArray seed)
  99 {
 100     ECPrivateKey *privKey = NULL; // contains both public and private values
 101     ECParams *ecparams = NULL;
 102     SECKEYECParams params_item;
 103     jint jSeedLength;
 104     jbyte* pSeedBuffer = NULL;
 105     jobjectArray result = NULL;
 106     jclass baCls = NULL;
 107     jbyteArray jba;
 108 
 109     // Initialize the ECParams struct
 110     params_item.len = env->GetArrayLength(encodedParams);
 111     params_item.data =
 112         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 113     if (params_item.data == NULL) {
 114         goto cleanup;
 115     }
 116 
 117     // Fill a new ECParams using the supplied OID
 118     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 119         /* bad curve OID */
 120         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 121         goto cleanup;
 122     }
 123 
 124     // Copy seed from Java to native buffer
 125     jSeedLength = env->GetArrayLength(seed);
 126     pSeedBuffer = new jbyte[jSeedLength];
 127     env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
 128 
 129     // Generate the new keypair (using the supplied seed)
 130     if (EC_NewKey(ecparams, &privKey, (unsigned char *) pSeedBuffer,
 131         jSeedLength, 0) != SECSuccess) {
 132         ThrowException(env, KEY_EXCEPTION);
 133         goto cleanup;
 134     }
 135 
 136     jboolean isCopy;
 137     baCls = env->FindClass("[B");
 138     if (baCls == NULL) {
 139         goto cleanup;
 140     }
 141     result = env->NewObjectArray(2, baCls, NULL);
 142     if (result == NULL) {
 143         goto cleanup;
 144     }
 145     jba = getEncodedBytes(env, &(privKey->privateValue));
 146     if (jba == NULL) {
 147         result = NULL;
 148         goto cleanup;
 149     }
 150     env->SetObjectArrayElement(result, 0, jba); // big integer
 151     if (env->ExceptionCheck()) { // should never happen
 152         result = NULL;
 153         goto cleanup;
 154     }
 155 
 156     jba = getEncodedBytes(env, &(privKey->publicValue));
 157     if (jba == NULL) {
 158         result = NULL;
 159         goto cleanup;
 160     }
 161     env->SetObjectArrayElement(result, 1, jba); // encoded ec point
 162     if (env->ExceptionCheck()) { // should never happen
 163         result = NULL;
 164         goto cleanup;
 165     }
 166 
 167 cleanup:
 168     {
 169         if (params_item.data) {
 170             env->ReleaseByteArrayElements(encodedParams,
 171                 (jbyte *) params_item.data, JNI_ABORT);
 172         }
 173         if (ecparams) {
 174             FreeECParams(ecparams, true);
 175         }
 176         if (privKey) {
 177             FreeECParams(&privKey->ecParams, false);
 178             SECITEM_FreeItem(&privKey->version, B_FALSE);
 179             SECITEM_FreeItem(&privKey->privateValue, B_FALSE);
 180             SECITEM_FreeItem(&privKey->publicValue, B_FALSE);
 181             free(privKey);
 182         }
 183 
 184         if (pSeedBuffer) {
 185             delete [] pSeedBuffer;
 186         }
 187     }
 188 
 189     return result;
 190 }
 191 
 192 /*
 193  * Class:     sun_security_ec_ECDSASignature
 194  * Method:    signDigest
 195  * Signature: ([B[B[B[B)[B
 196  */
 197 JNIEXPORT jbyteArray
 198 JNICALL Java_sun_security_ec_ECDSASignature_signDigest
 199   (JNIEnv *env, jclass clazz, jbyteArray digest, jbyteArray privateKey, jbyteArray encodedParams, jbyteArray seed)
 200 {
 201     jbyte* pDigestBuffer = NULL;
 202     jint jDigestLength = env->GetArrayLength(digest);
 203     jbyteArray jSignedDigest = NULL;
 204 
 205     SECItem signature_item;
 206     jbyte* pSignedDigestBuffer = NULL;
 207     jbyteArray temp;
 208 
 209     jint jSeedLength = env->GetArrayLength(seed);
 210     jbyte* pSeedBuffer = NULL;
 211 
 212     // Copy digest from Java to native buffer
 213     pDigestBuffer = new jbyte[jDigestLength];
 214     env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
 215     SECItem digest_item;
 216     digest_item.data = (unsigned char *) pDigestBuffer;
 217     digest_item.len = jDigestLength;
 218 
 219     ECPrivateKey privKey;
 220     privKey.privateValue.data = NULL;
 221 
 222     // Initialize the ECParams struct
 223     ECParams *ecparams = NULL;
 224     SECKEYECParams params_item;
 225     params_item.len = env->GetArrayLength(encodedParams);
 226     params_item.data =
 227         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 228     if (params_item.data == NULL) {
 229         goto cleanup;
 230     }
 231 
 232     // Fill a new ECParams using the supplied OID
 233     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 234         /* bad curve OID */
 235         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 236         goto cleanup;
 237     }
 238 
 239     // Extract private key data
 240     privKey.ecParams = *ecparams; // struct assignment
 241     privKey.privateValue.len = env->GetArrayLength(privateKey);
 242     privKey.privateValue.data =
 243         (unsigned char *) env->GetByteArrayElements(privateKey, 0);
 244     if (privKey.privateValue.data == NULL) {
 245         goto cleanup;
 246     }
 247 
 248     // Prepare a buffer for the signature (twice the key length)
 249     pSignedDigestBuffer = new jbyte[ecparams->order.len * 2];
 250     signature_item.data = (unsigned char *) pSignedDigestBuffer;
 251     signature_item.len = ecparams->order.len * 2;
 252 
 253     // Copy seed from Java to native buffer
 254     pSeedBuffer = new jbyte[jSeedLength];
 255     env->GetByteArrayRegion(seed, 0, jSeedLength, pSeedBuffer);
 256 
 257     // Sign the digest (using the supplied seed)
 258     if (ECDSA_SignDigest(&privKey, &signature_item, &digest_item,
 259         (unsigned char *) pSeedBuffer, jSeedLength, 0) != SECSuccess) {
 260         ThrowException(env, KEY_EXCEPTION);
 261         goto cleanup;
 262     }
 263 
 264     // Create new byte array
 265     temp = env->NewByteArray(signature_item.len);
 266     if (temp == NULL) {
 267         goto cleanup;
 268     }
 269 
 270     // Copy data from native buffer
 271     env->SetByteArrayRegion(temp, 0, signature_item.len, pSignedDigestBuffer);
 272     jSignedDigest = temp;
 273 
 274 cleanup:
 275     {
 276         if (params_item.data) {
 277             env->ReleaseByteArrayElements(encodedParams,
 278                 (jbyte *) params_item.data, JNI_ABORT);
 279         }
 280         if (privKey.privateValue.data) {
 281             env->ReleaseByteArrayElements(privateKey,
 282                 (jbyte *) privKey.privateValue.data, JNI_ABORT);
 283         }
 284         if (pDigestBuffer) {
 285             delete [] pDigestBuffer;
 286         }
 287         if (pSignedDigestBuffer) {
 288             delete [] pSignedDigestBuffer;
 289         }
 290         if (pSeedBuffer) {
 291             delete [] pSeedBuffer;
 292         }
 293         if (ecparams) {
 294             FreeECParams(ecparams, true);
 295         }
 296     }
 297 
 298     return jSignedDigest;
 299 }
 300 
 301 /*
 302  * Class:     sun_security_ec_ECDSASignature
 303  * Method:    verifySignedDigest
 304  * Signature: ([B[B[B[B)Z
 305  */
 306 JNIEXPORT jboolean
 307 JNICALL Java_sun_security_ec_ECDSASignature_verifySignedDigest
 308   (JNIEnv *env, jclass clazz, jbyteArray signedDigest, jbyteArray digest, jbyteArray publicKey, jbyteArray encodedParams)
 309 {
 310     jboolean isValid = false;
 311 
 312     // Copy signedDigest from Java to native buffer
 313     jbyte* pSignedDigestBuffer = NULL;
 314     jint jSignedDigestLength = env->GetArrayLength(signedDigest);
 315     pSignedDigestBuffer = new jbyte[jSignedDigestLength];
 316     env->GetByteArrayRegion(signedDigest, 0, jSignedDigestLength,
 317         pSignedDigestBuffer);
 318     SECItem signature_item;
 319     signature_item.data = (unsigned char *) pSignedDigestBuffer;
 320     signature_item.len = jSignedDigestLength;
 321 
 322     // Copy digest from Java to native buffer
 323     jbyte* pDigestBuffer = NULL;
 324     jint jDigestLength = env->GetArrayLength(digest);
 325     pDigestBuffer = new jbyte[jDigestLength];
 326     env->GetByteArrayRegion(digest, 0, jDigestLength, pDigestBuffer);
 327     SECItem digest_item;
 328     digest_item.data = (unsigned char *) pDigestBuffer;
 329     digest_item.len = jDigestLength;
 330 
 331     // Extract public key data
 332     ECPublicKey pubKey;
 333     pubKey.publicValue.data = NULL;
 334     ECParams *ecparams = NULL;
 335     SECKEYECParams params_item;
 336 
 337     // Initialize the ECParams struct
 338     params_item.len = env->GetArrayLength(encodedParams);
 339     params_item.data =
 340         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 341     if (params_item.data == NULL) {
 342         goto cleanup;
 343     }
 344 
 345     // Fill a new ECParams using the supplied OID
 346     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 347         /* bad curve OID */
 348         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 349         goto cleanup;
 350     }
 351     pubKey.ecParams = *ecparams; // struct assignment
 352     pubKey.publicValue.len = env->GetArrayLength(publicKey);
 353     pubKey.publicValue.data =
 354         (unsigned char *) env->GetByteArrayElements(publicKey, 0);
 355 
 356     if (ECDSA_VerifyDigest(&pubKey, &signature_item, &digest_item, 0)
 357             != SECSuccess) {
 358         goto cleanup;
 359     }
 360 
 361     isValid = true;
 362 
 363 cleanup:
 364     {
 365         if (params_item.data)
 366             env->ReleaseByteArrayElements(encodedParams,
 367                 (jbyte *) params_item.data, JNI_ABORT);
 368 
 369         if (pubKey.publicValue.data)
 370             env->ReleaseByteArrayElements(publicKey,
 371                 (jbyte *) pubKey.publicValue.data, JNI_ABORT);
 372 
 373         if (ecparams)
 374             FreeECParams(ecparams, true);
 375 
 376         if (pSignedDigestBuffer)
 377             delete [] pSignedDigestBuffer;
 378 
 379         if (pDigestBuffer)
 380             delete [] pDigestBuffer;
 381     }
 382 
 383     return isValid;
 384 }
 385 
 386 /*
 387  * Class:     sun_security_ec_ECDHKeyAgreement
 388  * Method:    deriveKey
 389  * Signature: ([B[B[B)[B
 390  */
 391 JNIEXPORT jbyteArray
 392 JNICALL Java_sun_security_ec_ECDHKeyAgreement_deriveKey
 393   (JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey, jbyteArray encodedParams)
 394 {
 395     jbyteArray jSecret = NULL;
 396     ECParams *ecparams = NULL;
 397     SECItem privateValue_item;
 398     privateValue_item.data = NULL;
 399     SECItem publicValue_item;
 400     publicValue_item.data = NULL;
 401     SECKEYECParams params_item;
 402     params_item.data = NULL;
 403 
 404     // Extract private key value
 405     privateValue_item.len = env->GetArrayLength(privateKey);
 406     privateValue_item.data =
 407             (unsigned char *) env->GetByteArrayElements(privateKey, 0);
 408     if (privateValue_item.data == NULL) {
 409         goto cleanup;
 410     }
 411 
 412     // Extract public key value
 413     publicValue_item.len = env->GetArrayLength(publicKey);
 414     publicValue_item.data =
 415         (unsigned char *) env->GetByteArrayElements(publicKey, 0);
 416     if (publicValue_item.data == NULL) {
 417         goto cleanup;
 418     }
 419 
 420     // Initialize the ECParams struct
 421     params_item.len = env->GetArrayLength(encodedParams);
 422     params_item.data =
 423         (unsigned char *) env->GetByteArrayElements(encodedParams, 0);
 424     if (params_item.data == NULL) {
 425         goto cleanup;
 426     }
 427 
 428     // Fill a new ECParams using the supplied OID
 429     if (EC_DecodeParams(&params_item, &ecparams, 0) != SECSuccess) {
 430         /* bad curve OID */
 431         ThrowException(env, INVALID_ALGORITHM_PARAMETER_EXCEPTION);
 432         goto cleanup;
 433     }
 434 
 435     // Prepare a buffer for the secret
 436     SECItem secret_item;
 437     secret_item.data = NULL;
 438     secret_item.len = ecparams->order.len * 2;
 439 
 440     if (ECDH_Derive(&publicValue_item, ecparams, &privateValue_item, B_FALSE,
 441         &secret_item, 0) != SECSuccess) {
 442         ThrowException(env, ILLEGAL_STATE_EXCEPTION);
 443         goto cleanup;
 444     }
 445 
 446     // Create new byte array
 447     jSecret = env->NewByteArray(secret_item.len);
 448     if (jSecret == NULL) {
 449         goto cleanup;
 450     }
 451 
 452     // Copy bytes from the SECItem buffer to a Java byte array
 453     env->SetByteArrayRegion(jSecret, 0, secret_item.len,
 454         (jbyte *)secret_item.data);
 455 
 456     // Free the SECItem data buffer
 457     SECITEM_FreeItem(&secret_item, B_FALSE);
 458 
 459 cleanup:
 460     {
 461         if (privateValue_item.data)
 462             env->ReleaseByteArrayElements(privateKey,
 463                 (jbyte *) privateValue_item.data, JNI_ABORT);
 464 
 465         if (publicValue_item.data)
 466             env->ReleaseByteArrayElements(publicKey,
 467                 (jbyte *) publicValue_item.data, JNI_ABORT);
 468 
 469         if (params_item.data)
 470             env->ReleaseByteArrayElements(encodedParams,
 471                 (jbyte *) params_item.data, JNI_ABORT);
 472 
 473         if (ecparams)
 474             FreeECParams(ecparams, true);
 475     }
 476 
 477     return jSecret;
 478 }
 479 
 480 } /* extern "C" */