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