diff --git a/native/com_wolfssl_WolfSSLCRL.c b/native/com_wolfssl_WolfSSLCRL.c index 2000727..edcc9b8 100644 --- a/native/com_wolfssl_WolfSSLCRL.c +++ b/native/com_wolfssl_WolfSSLCRL.c @@ -451,11 +451,12 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCRL_X509_1CRL_1sign } } - /* sign WOLFSSL_X509_CRL with WOLFSSL_EVP_PKEY, returns size of signature - * on success or negative on error */ + /* sign WOLFSSL_X509_CRL with WOLFSSL_EVP_PKEY, wolfSSL_X509_CRL_sign() + * returns WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE (0) or negative + * code on error */ if (ret == WOLFSSL_SUCCESS) { ret = wolfSSL_X509_CRL_sign(crl, priv, md); - if (ret >= 0) { + if (ret > 0) { ret = WOLFSSL_SUCCESS; } } diff --git a/native/com_wolfssl_WolfSSLCertRequest.c b/native/com_wolfssl_WolfSSLCertRequest.c index a2c0245..9d87cd1 100644 --- a/native/com_wolfssl_WolfSSLCertRequest.c +++ b/native/com_wolfssl_WolfSSLCertRequest.c @@ -327,13 +327,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertRequest_X509_1REQ_1sign } } - /* sign WOLFSSL_X509 with WOLFSSL_EVP_PKEY, returns size of signature - * on success or negative on error */ + /* sign X509 REQ with WOLFSSL_EVP_PKEY, wolfSSL_X509_REQ_sign() returns + * WOLFSSL_SUCCESS on success or WOLFSSL_FAILURE (0) on error */ if (ret == WOLFSSL_SUCCESS) { ret = wolfSSL_X509_REQ_sign(x509, priv, md); - if (ret >= 0) { + if (ret > 0) { ret = WOLFSSL_SUCCESS; } + else { + ret = WOLFSSL_FAILURE; + } } if (priv != NULL) { diff --git a/native/com_wolfssl_WolfSSLCertificate.c b/native/com_wolfssl_WolfSSLCertificate.c index 95f5cf9..e2261b6 100644 --- a/native/com_wolfssl_WolfSSLCertificate.c +++ b/native/com_wolfssl_WolfSSLCertificate.c @@ -975,13 +975,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1sign ret = wolfSSL_X509_set_version(x509, 2L); } - /* sign WOLFSSL_X509 with WOLFSSL_EVP_PKEY, returns size of signature - * on success or negative on error */ + /* sign WOLFSSL_X509 with WOLFSSL_EVP_PKEY, wolfSSL_X509_sign() returns + * signature size on success or WOLFSSL_FAILURE (0) on error */ if (ret == WOLFSSL_SUCCESS) { ret = wolfSSL_X509_sign(x509, priv, md); - if (ret >= 0) { + if (ret > 0) { ret = WOLFSSL_SUCCESS; } + else { + ret = WOLFSSL_FAILURE; + } } if (priv != NULL) { diff --git a/native/com_wolfssl_WolfSSLContext.c b/native/com_wolfssl_WolfSSLContext.c index 003707e..b992ad1 100644 --- a/native/com_wolfssl_WolfSSLContext.c +++ b/native/com_wolfssl_WolfSSLContext.c @@ -935,13 +935,13 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLContext_memsaveCertCache { #ifdef PERSIST_CERT_CACHE int ret; - int usedTmp; + int usedTmp = 0; unsigned char* memBuf = NULL; WOLFSSL_CTX* ctx = (WOLFSSL_CTX*)(uintptr_t)ctxPtr; jclass excClass = NULL; (void)jcl; - if (jenv == NULL || ctx == NULL || mem == NULL || sz <= 0) { + if (jenv == NULL || ctx == NULL || mem == NULL || used == NULL || sz <= 0) { return (jint)BAD_FUNC_ARG; } @@ -961,23 +961,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLContext_memsaveCertCache ret = wolfSSL_CTX_memsave_cert_cache(ctx, memBuf, (int)sz, &usedTmp); - /* set used value for return */ - (*jenv)->SetIntArrayRegion(jenv, used, 0, 1, &usedTmp); - if ((*jenv)->ExceptionOccurred(jenv)) { - (*jenv)->ExceptionDescribe(jenv); - (*jenv)->ExceptionClear(jenv); + /* only publish used and mem on success, usedTmp is not set on error */ + if (ret == WOLFSSL_SUCCESS) { + /* fail if native reported size is outside memBuf bounds */ + if ((usedTmp < 0) || (usedTmp > (int)sz)) { + XFREE(memBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return (jint)SSL_FAILURE; + } - XFREE(memBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); - - (*jenv)->ThrowNew(jenv, excClass, - "Failed to set array region in native memsaveCertCache"); - - return (jint)SSL_FAILURE; - } - - /* set jbyteArray for return */ - if (usedTmp >= 0) { - (*jenv)->SetByteArrayRegion(jenv, mem, 0, usedTmp, (jbyte*)memBuf); + /* set used value for return */ + (*jenv)->SetIntArrayRegion(jenv, used, 0, 1, &usedTmp); if ((*jenv)->ExceptionOccurred(jenv)) { (*jenv)->ExceptionDescribe(jenv); (*jenv)->ExceptionClear(jenv); @@ -985,10 +978,26 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLContext_memsaveCertCache XFREE(memBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); (*jenv)->ThrowNew(jenv, excClass, - "Failed to set byte region in native memsaveCertCache"); + "Failed to set array region in native memsaveCertCache"); return (jint)SSL_FAILURE; } + + /* set jbyteArray for return */ + if (usedTmp > 0) { + (*jenv)->SetByteArrayRegion(jenv, mem, 0, usedTmp, (jbyte*)memBuf); + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + + XFREE(memBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + (*jenv)->ThrowNew(jenv, excClass, + "Failed to set byte region in native memsaveCertCache"); + + return (jint)SSL_FAILURE; + } + } } XFREE(memBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -1318,6 +1327,7 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx) jobject ctxRef; /* WolfSSLContext object */ jclass innerCtxClass; /* WolfSSLContext class */ + jmethodID recvCbMethodId; /* internalIORecvCallback ID */ jbyteArray inData; if (!g_vm || !ssl || !buf || !ctx) { @@ -1429,10 +1439,17 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx) return WOLFSSL_CBIO_ERR_GENERAL; } - /* make sure cached recv callback method ID is not null */ - if (!g_sslIORecvMethodId) { + /* call internal I/O recv callback */ + recvCbMethodId = (*jenv)->GetMethodID(jenv, innerCtxClass, + "internalIORecvCallback", "(Lcom/wolfssl/WolfSSLSession;[BI)I"); + if (!recvCbMethodId) { + if ((*jenv)->ExceptionOccurred(jenv)) { + (*jenv)->ExceptionDescribe(jenv); + (*jenv)->ExceptionClear(jenv); + } (*jenv)->ThrowNew(jenv, excClass, - "Cached recv callback method ID is null in NativeIORecvCb"); + "Error getting internalIORecvCallback method from JNI"); + (*jenv)->DeleteLocalRef(jenv, ctxRef); if (needsDetach) (*g_vm)->DetachCurrentThread(g_vm); return WOLFSSL_CBIO_ERR_GENERAL; @@ -1449,11 +1466,9 @@ int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx) return WOLFSSL_CBIO_ERR_GENERAL; } - /* call Java send callback, ignore native ctx since Java - * handles it */ - retval = (*jenv)->CallIntMethod(jenv, ctxRef, g_sslIORecvMethodId, - (jobject)(*g_cachedSSLObj), - inData, (jint)sz); + /* call Java recv callback, ignore native ctx since Java handles it */ + retval = (*jenv)->CallIntMethod(jenv, ctxRef, recvCbMethodId, + (jobject)(*g_cachedSSLObj), inData, (jint)sz); if ((*jenv)->ExceptionOccurred(jenv)) { (*jenv)->ExceptionDescribe(jenv); diff --git a/src/test/com/wolfssl/test/WolfSSLCRLTest.java b/src/test/com/wolfssl/test/WolfSSLCRLTest.java index 13ecfb4..3e1f12f 100644 --- a/src/test/com/wolfssl/test/WolfSSLCRLTest.java +++ b/src/test/com/wolfssl/test/WolfSSLCRLTest.java @@ -466,7 +466,7 @@ public class WolfSSLCRLTest { /* Sign CRL */ int ret = crl.sign(privKey, "SHA256"); - assertTrue("sign should succeed", ret >= 0); + assertEquals("sign should succeed", WolfSSL.SSL_SUCCESS, ret); byte[] sig = crl.getSignature(); assertNotNull("signature should be available after sign", sig); assertTrue("signature length should be > 0", sig.length > 0); @@ -484,6 +484,27 @@ public class WolfSSLCRLTest { crl.free(); } + @Test + public void testSignEmptyCrlFails() + throws WolfSSLException, WolfSSLJNIException, + NoSuchAlgorithmException { + + Assume.assumeTrue(WolfSSL.CrlGenerationEnabled()); + + WolfSSLCRL crl = new WolfSSLCRL(); + assertNotNull(crl); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + KeyPair keyPair = kpg.generateKeyPair(); + + int ret = crl.sign(keyPair.getPrivate(), "SHA256"); + assertEquals("sign of empty CRL should fail", + WolfSSL.BAD_FUNC_ARG, ret); + + crl.free(); + } + @Test public void testSign_NativePemKeyBytesRegression() throws WolfSSLException, WolfSSLJNIException, IOException, diff --git a/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java b/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java index 66f4232..765d70f 100644 --- a/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java +++ b/src/test/com/wolfssl/test/WolfSSLCertRequestTest.java @@ -618,6 +618,35 @@ public class WolfSSLCertRequestTest { req.free(); } + @Test + public void testSignRequestFailureThrows() + throws WolfSSLException, WolfSSLJNIException, IOException { + + Assume.assumeTrue(WolfSSL.certReqEnabled()); + + WolfSSLCertRequest req = new WolfSSLCertRequest(); + assertNotNull(req); + + WolfSSLX509Name subjectName = GenerateTestSubjectName(); + assertNotNull(subjectName); + + try { + req.setSubjectName(subjectName); + + /* no public key set, native signing fails */ + try { + req.signRequest(cliKeyDer, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1, "SHA256"); + fail("signRequest() should throw when native signing fails"); + } catch (WolfSSLException expected) { + /* expected */ + } + } finally { + subjectName.free(); + req.free(); + } + } + @Test public void testGenCSR_UsingBuffers() throws WolfSSLException, WolfSSLJNIException, IOException, diff --git a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java index d10ba28..cf561a2 100644 --- a/src/test/com/wolfssl/test/WolfSSLCertificateTest.java +++ b/src/test/com/wolfssl/test/WolfSSLCertificateTest.java @@ -797,6 +797,43 @@ public class WolfSSLCertificateTest { } } + @Test + public void testCertSignFailureThrows() + throws WolfSSLException, WolfSSLJNIException, IOException { + + WolfSSLCertificate x509 = null; + WolfSSLX509Name name = null; + + if (WolfSSL.FileSystemEnabled() == false) { + return; + } + + x509 = new WolfSSLCertificate(); + name = new WolfSSLX509Name(); + + try { + Instant now = Instant.now(); + x509.setNotBefore(Date.from(now)); + x509.setNotAfter(Date.from(now.plus(Duration.ofDays(365)))); + x509.setSerialNumber(BigInteger.valueOf(1123)); + + name.setCommonName("sign failure test"); + x509.setSubjectName(name); + + /* no public key set, native signing fails */ + try { + x509.signCert(cliKeyDer, WolfSSL.RSAk, + WolfSSL.SSL_FILETYPE_ASN1, "SHA256"); + fail("signCert() should throw when native signing fails"); + } catch (WolfSSLException expected) { + /* expected */ + } + } finally { + name.free(); + x509.free(); + } + } + /* Generate a self-signed cert with the given CN and an optional single * SubjectAltName entry, return its DER encoding. */ private byte[] genIpTestCertDer(String cn, String sanValue, int sanType) diff --git a/src/test/com/wolfssl/test/WolfSSLContextTest.java b/src/test/com/wolfssl/test/WolfSSLContextTest.java index 33f6968..71e492b 100644 --- a/src/test/com/wolfssl/test/WolfSSLContextTest.java +++ b/src/test/com/wolfssl/test/WolfSSLContextTest.java @@ -46,6 +46,8 @@ import java.util.concurrent.TimeUnit; import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLContext; import com.wolfssl.WolfSSLException; +import com.wolfssl.WolfSSLIORecvCallback; +import com.wolfssl.WolfSSLIOSendCallback; import com.wolfssl.WolfSSLJNIException; import com.wolfssl.WolfSSLVerifyCallback; import com.wolfssl.WolfSSLMissingCRLCallback; @@ -249,6 +251,206 @@ public class WolfSSLContextTest { } } + @Test + public void test_WolfSSLContext_memsaveCertCache() + throws WolfSSLException, WolfSSLJNIException { + + int ret; + int sz; + int[] used = new int[1]; + byte[] mem = null; + WolfSSLContext ctx2 = null; + + if (WolfSSL.FileSystemEnabled() == false) { + return; + } + + ret = ctx.loadVerifyLocations(caCert, null); + assertEquals(WolfSSL.SSL_SUCCESS, ret); + + sz = ctx.getCertCacheMemsize(); + if (sz == WolfSSL.NOT_COMPILED_IN) { + /* skip when PERSIST_CERT_CACHE is not compiled in */ + return; + } + assertTrue(sz > 0); + + /* undersized buffer returns BUFFER_E and leaves used untouched */ + used[0] = -1; + ret = ctx.memsaveCertCache(new byte[1], 1, used); + assertEquals(WolfSSL.BUFFER_E, ret); + assertEquals(-1, used[0]); + + /* null used array is rejected */ + ret = ctx.memsaveCertCache(new byte[sz], sz, null); + assertEquals(WolfSSL.BAD_FUNC_ARG, ret); + + /* correctly sized buffer saves cert cache */ + mem = new byte[sz]; + used[0] = 0; + ret = ctx.memsaveCertCache(mem, sz, used); + assertEquals(WolfSSL.SSL_SUCCESS, ret); + assertTrue(used[0] > 0 && used[0] <= sz); + + /* saved cache restores into a new context */ + ctx2 = new WolfSSLContext(WolfSSL.SSLv23_ServerMethod()); + try { + ret = ctx2.memrestoreCertCache(mem, used[0]); + assertEquals(WolfSSL.SSL_SUCCESS, ret); + } finally { + ctx2.free(); + } + } + + /* in-memory byte queue connecting context level I/O callbacks */ + class TestIOQueue { + private byte[] data = new byte[0]; + + synchronized void add(byte[] buf, int sz) { + byte[] tmp = new byte[data.length + sz]; + System.arraycopy(data, 0, tmp, 0, data.length); + System.arraycopy(buf, 0, tmp, data.length, sz); + data = tmp; + } + + synchronized int take(byte[] buf, int sz) { + byte[] tmp = null; + int n = Math.min(sz, data.length); + if (n == 0) { + return 0; + } + System.arraycopy(data, 0, buf, 0, n); + tmp = new byte[data.length - n]; + System.arraycopy(data, n, tmp, 0, tmp.length); + data = tmp; + return n; + } + } + + class TestCtxIOCallback implements WolfSSLIORecvCallback, + WolfSSLIOSendCallback { + + private final TestIOQueue in; + private final TestIOQueue out; + + TestCtxIOCallback(TestIOQueue in, TestIOQueue out) { + this.in = in; + this.out = out; + } + + public int receiveCallback(WolfSSLSession ssl, byte[] buf, int sz, + Object ctx) { + int n = in.take(buf, sz); + if (n == 0) { + return WolfSSL.WOLFSSL_CBIO_ERR_WANT_READ; + } + return n; + } + + public int sendCallback(WolfSSLSession ssl, byte[] buf, int sz, + Object ctx) { + out.add(buf, sz); + return sz; + } + } + + @Test + public void test_WolfSSLContext_ioRecvSendCallbacks() throws Exception { + + int cliRet = WolfSSL.SSL_FAILURE; + int srvRet = WolfSSL.SSL_FAILURE; + int cliErr = 0; + int srvErr = 0; + int i; + WolfSSLContext srvCtx = null; + WolfSSLContext cliCtx = null; + WolfSSLSession server = null; + WolfSSLSession client = null; + TestIOQueue cliToSrv = new TestIOQueue(); + TestIOQueue srvToCli = new TestIOQueue(); + TestCtxIOCallback srvCb = new TestCtxIOCallback(cliToSrv, srvToCli); + TestCtxIOCallback cliCb = new TestCtxIOCallback(srvToCli, cliToSrv); + + if (WolfSSL.FileSystemEnabled() == false) { + return; + } + + try { + srvCtx = new WolfSSLContext(WolfSSL.SSLv23_ServerMethod()); + cliCtx = new WolfSSLContext(WolfSSL.SSLv23_ClientMethod()); + + assertEquals(WolfSSL.SSL_SUCCESS, srvCtx.useCertificateFile( + svrCert, WolfSSL.SSL_FILETYPE_PEM)); + assertEquals(WolfSSL.SSL_SUCCESS, srvCtx.usePrivateKeyFile( + svrKey, WolfSSL.SSL_FILETYPE_PEM)); + assertEquals(WolfSSL.SSL_SUCCESS, + cliCtx.loadVerifyLocations(caCert, null)); + + /* register context level I/O callbacks before creating sessions + * so new sessions inherit them */ + srvCtx.setIORecv(srvCb); + srvCtx.setIOSend(srvCb); + cliCtx.setIORecv(cliCb); + cliCtx.setIOSend(cliCb); + + server = new WolfSSLSession(srvCtx); + client = new WolfSSLSession(cliCtx); + + /* single threaded handshake, alternate client and server until + * both complete */ + for (i = 0; i < 100; i++) { + if (cliRet != WolfSSL.SSL_SUCCESS) { + cliRet = client.connect(); + cliErr = client.getError(cliRet); + if (cliRet != WolfSSL.SSL_SUCCESS && + cliErr != WolfSSL.SSL_ERROR_WANT_READ && + cliErr != WolfSSL.SSL_ERROR_WANT_WRITE) { + break; + } + } + if (srvRet != WolfSSL.SSL_SUCCESS) { + srvRet = server.accept(); + srvErr = server.getError(srvRet); + if (srvRet != WolfSSL.SSL_SUCCESS && + srvErr != WolfSSL.SSL_ERROR_WANT_READ && + srvErr != WolfSSL.SSL_ERROR_WANT_WRITE) { + break; + } + } + if (cliRet == WolfSSL.SSL_SUCCESS && + srvRet == WolfSSL.SSL_SUCCESS) { + break; + } + } + + assertEquals("client connect over context I/O callbacks, " + + "error: " + cliErr, WolfSSL.SSL_SUCCESS, cliRet); + assertEquals("server accept over context I/O callbacks, " + + "error: " + srvErr, WolfSSL.SSL_SUCCESS, srvRet); + + /* exchange application data through the callbacks */ + byte[] msg = "hello callbacks".getBytes(); + byte[] rcvd = new byte[msg.length]; + assertEquals(msg.length, client.write(msg, msg.length)); + assertEquals(msg.length, server.read(rcvd, rcvd.length)); + assertArrayEquals(msg, rcvd); + + } finally { + if (server != null) { + server.freeSSL(); + } + if (client != null) { + client.freeSSL(); + } + if (srvCtx != null) { + srvCtx.free(); + } + if (cliCtx != null) { + cliCtx.free(); + } + } + } + class TestPskClientCb implements WolfSSLPskClientCallback { public long pskClientCallback(WolfSSLSession ssl, String hint,