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