Skip to content

Commit 4b6fbf1

Browse files
committed
JNI: add NULL checks before curve25519 key dereference in export functions, add Curve25519 JUnit tests
1 parent 7edf7f3 commit 4b6fbf1

5 files changed

Lines changed: 227 additions & 10 deletions

File tree

jni/include/com_wolfssl_wolfcrypt_Curve25519.h

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jni/jni_curve25519.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1export_1private(
278278
return NULL;
279279
}
280280

281+
if (curve25519 == NULL) {
282+
throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG);
283+
return NULL;
284+
}
285+
281286
outputSz = wc_curve25519_size(curve25519);
282287

283288
output = XMALLOC(outputSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
@@ -287,9 +292,7 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1export_1private(
287292
}
288293
XMEMSET(output, 0, outputSz);
289294

290-
ret = (!curve25519)
291-
? BAD_FUNC_ARG
292-
: wc_curve25519_export_private_raw(curve25519, output, &outputSz);
295+
ret = wc_curve25519_export_private_raw(curve25519, output, &outputSz);
293296

294297
if (ret == 0) {
295298
result = (*env)->NewByteArray(env, outputSz);
@@ -334,6 +337,11 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1export_1public (
334337
return NULL;
335338
}
336339

340+
if (curve25519 == NULL) {
341+
throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG);
342+
return NULL;
343+
}
344+
337345
outputSz = wc_curve25519_size(curve25519);
338346

339347
output = XMALLOC(outputSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
@@ -343,9 +351,7 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1export_1public (
343351
}
344352
XMEMSET(output, 0, outputSz);
345353

346-
ret = (!curve25519)
347-
? BAD_FUNC_ARG
348-
: wc_curve25519_export_public(curve25519, output, &outputSz);
354+
ret = wc_curve25519_export_public(curve25519, output, &outputSz);
349355

350356
if (ret == 0) {
351357
result = (*env)->NewByteArray(env, outputSz);
@@ -397,18 +403,21 @@ Java_com_wolfssl_wolfcrypt_Curve25519_wc_1curve25519_1make_1shared_1secret(
397403
return NULL;
398404
}
399405

406+
if (curve25519 == NULL || pub == NULL) {
407+
throwWolfCryptExceptionFromError(env, BAD_FUNC_ARG);
408+
return NULL;
409+
}
410+
400411
outputSz = wc_curve25519_size(curve25519);
401412
output = XMALLOC(outputSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
402413
if (output == NULL) {
403414
throwOutOfMemoryException(env,
404-
"Failed to allocate shared secret buffer");
415+
"Failed to allocate shared secret buffer");
405416
return result;
406417
}
407418
XMEMSET(output, 0, outputSz);
408419

409-
ret = (!curve25519 || !pub)
410-
? BAD_FUNC_ARG
411-
: wc_curve25519_shared_secret(curve25519, pub, output, &outputSz);
420+
ret = wc_curve25519_shared_secret(curve25519, pub, output, &outputSz);
412421

413422
if (ret == 0) {
414423
result = (*env)->NewByteArray(env, outputSz);

src/main/java/com/wolfssl/wolfcrypt/Curve25519.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ public class Curve25519 extends NativeStruct {
3131
/** Lock around object state */
3232
protected final Object stateLock = new Object();
3333

34+
/**
35+
* Curve25519 key size, from native CURVE25519_KEYSIZE.
36+
*/
37+
public static final int CURVE25519_KEY_SIZE = 32;
38+
3439
/**
3540
* Create new Curve25519 object.
3641
*
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/* Curve25519Test.java
2+
*
3+
* Copyright (C) 2006-2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfSSL.
6+
*
7+
* wolfSSL is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 2 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfSSL is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20+
*/
21+
22+
package com.wolfssl.wolfcrypt.test;
23+
24+
import static org.junit.Assert.*;
25+
26+
import org.junit.Assume;
27+
import org.junit.BeforeClass;
28+
import org.junit.Test;
29+
import org.junit.Rule;
30+
import org.junit.rules.TestRule;
31+
32+
import com.wolfssl.wolfcrypt.Curve25519;
33+
import com.wolfssl.wolfcrypt.Rng;
34+
import com.wolfssl.wolfcrypt.test.TimedTestWatcher;
35+
import com.wolfssl.wolfcrypt.NativeStruct;
36+
import com.wolfssl.wolfcrypt.WolfCryptError;
37+
import com.wolfssl.wolfcrypt.WolfCryptException;
38+
39+
public class Curve25519Test {
40+
41+
private static Rng rng = new Rng();
42+
private static final Object rngLock = new Object();
43+
44+
@Rule(order = Integer.MIN_VALUE)
45+
public TestRule testWatcher = TimedTestWatcher.create();
46+
47+
@BeforeClass
48+
public static void setUpRng() {
49+
synchronized (rngLock) {
50+
rng.init();
51+
}
52+
}
53+
54+
@BeforeClass
55+
public static void checkAvailability() {
56+
try {
57+
new Curve25519();
58+
System.out.println("JNI Curve25519 Class");
59+
60+
} catch (WolfCryptException e) {
61+
if (e.getError() == WolfCryptError.NOT_COMPILED_IN)
62+
System.out.println("Curve25519 test skipped: " + e.getError());
63+
Assume.assumeNoException(e);
64+
}
65+
}
66+
67+
/**
68+
* Skip test if WolfCryptException is NOT_COMPILED_IN, otherwise rethrow.
69+
*/
70+
private static void skipIfNotCompiledIn(WolfCryptException e) {
71+
72+
if (e.getError() == WolfCryptError.NOT_COMPILED_IN) {
73+
Assume.assumeNoException(e);
74+
}
75+
throw e;
76+
}
77+
78+
@Test
79+
public void constructorShouldNotInitializeNativeStruct() {
80+
assertEquals(NativeStruct.NULL, new Curve25519().getNativeStruct());
81+
}
82+
83+
@Test
84+
public void sharedSecretShouldMatch() {
85+
86+
Curve25519 alice = new Curve25519();
87+
Curve25519 bob = new Curve25519();
88+
89+
synchronized (rngLock) {
90+
alice.makeKey(rng, Curve25519.CURVE25519_KEY_SIZE);
91+
bob.makeKey(rng, Curve25519.CURVE25519_KEY_SIZE);
92+
}
93+
94+
byte[] secretA;
95+
byte[] secretB;
96+
try {
97+
secretA = alice.makeSharedSecret(bob);
98+
secretB = bob.makeSharedSecret(alice);
99+
} catch (WolfCryptException e) {
100+
alice.releaseNativeStruct();
101+
bob.releaseNativeStruct();
102+
skipIfNotCompiledIn(e);
103+
return;
104+
}
105+
106+
assertNotNull(secretA);
107+
assertNotNull(secretB);
108+
assertTrue(secretA.length > 0);
109+
assertArrayEquals(secretA, secretB);
110+
111+
alice.releaseNativeStruct();
112+
bob.releaseNativeStruct();
113+
}
114+
115+
@Test
116+
public void exportImportPublicAndSharedSecret() {
117+
118+
Curve25519 alice = new Curve25519();
119+
Curve25519 bob = new Curve25519();
120+
121+
synchronized (rngLock) {
122+
alice.makeKey(rng, Curve25519.CURVE25519_KEY_SIZE);
123+
bob.makeKey(rng, Curve25519.CURVE25519_KEY_SIZE);
124+
}
125+
126+
/* Export and reimport Alice's public key */
127+
byte[] alicePub;
128+
try {
129+
alicePub = alice.exportPublic();
130+
} catch (WolfCryptException e) {
131+
alice.releaseNativeStruct();
132+
bob.releaseNativeStruct();
133+
skipIfNotCompiledIn(e);
134+
return;
135+
}
136+
assertNotNull(alicePub);
137+
assertTrue(alicePub.length > 0);
138+
139+
Curve25519 alicePubOnly = new Curve25519();
140+
alicePubOnly.importPublic(alicePub);
141+
142+
/* Bob's shared secret with original and reimported should match */
143+
byte[] secret1;
144+
byte[] secret2;
145+
try {
146+
secret1 = bob.makeSharedSecret(alice);
147+
secret2 = bob.makeSharedSecret(alicePubOnly);
148+
} catch (WolfCryptException e) {
149+
alice.releaseNativeStruct();
150+
bob.releaseNativeStruct();
151+
alicePubOnly.releaseNativeStruct();
152+
skipIfNotCompiledIn(e);
153+
return;
154+
}
155+
assertArrayEquals(secret1, secret2);
156+
157+
alice.releaseNativeStruct();
158+
bob.releaseNativeStruct();
159+
alicePubOnly.releaseNativeStruct();
160+
}
161+
162+
@Test
163+
public void exportImportPrivateKey() {
164+
165+
Curve25519 origKey = new Curve25519();
166+
167+
synchronized (rngLock) {
168+
origKey.makeKey(rng, Curve25519.CURVE25519_KEY_SIZE);
169+
}
170+
171+
byte[] privKey;
172+
byte[] pubKey;
173+
try {
174+
privKey = origKey.exportPrivate();
175+
pubKey = origKey.exportPublic();
176+
} catch (WolfCryptException e) {
177+
origKey.releaseNativeStruct();
178+
skipIfNotCompiledIn(e);
179+
return;
180+
}
181+
assertNotNull(privKey);
182+
assertNotNull(pubKey);
183+
assertTrue(privKey.length > 0);
184+
assertTrue(pubKey.length > 0);
185+
186+
/* Import private + public into new key object */
187+
Curve25519 importedKey = new Curve25519();
188+
importedKey.importPrivate(privKey, pubKey);
189+
190+
/* Verify exported keys match original */
191+
byte[] reExportedPriv = importedKey.exportPrivate();
192+
byte[] reExportedPub = importedKey.exportPublic();
193+
assertArrayEquals(privKey, reExportedPriv);
194+
assertArrayEquals(pubKey, reExportedPub);
195+
196+
origKey.releaseNativeStruct();
197+
importedKey.releaseNativeStruct();
198+
}
199+
}
200+

src/test/java/com/wolfssl/wolfcrypt/test/WolfCryptTestSuite.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
DhTest.class,
5353
EccTest.class,
5454
Ed25519Test.class,
55+
Curve25519Test.class,
5556
WolfObjectTest.class,
5657
WolfSSLCertManagerOCSPTest.class,
5758
WolfSSLX509StoreCtxTest.class,

0 commit comments

Comments
 (0)