From 569b842867d8c30985b0e3ad7f2c73e34bceab7b Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 23 Jun 2022 17:24:36 -0600 Subject: [PATCH 1/2] SSLEngine: support large application data, refactor for better status messages --- .../wolfssl/provider/jsse/WolfSSLEngine.java | 793 ++++++++++++++---- .../jsse/WolfSSLImplementSSLSession.java | 16 +- .../provider/jsse/test/WolfSSLEngineTest.java | 71 ++ .../jsse/test/WolfSSLTestFactory.java | 8 +- 4 files changed, 715 insertions(+), 173 deletions(-) diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java index fc7856f7..fc9308a0 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java @@ -40,7 +40,17 @@ import java.net.SocketTimeoutException; /** - * wolfSSL implementation of SSLEngine + * wolfSSL implementation of SSLEngine. + * + * There is more verbose debugging available for this class apart + * from the normal 'wolfjsse.debug' logging. To enable more verbose + * logging use both the following system properties: + * + * System.setProperty("wolfjsse.debug", "true"); + * System.setProperty("wolfsslengine.debug", "true"); + * + * This will add extra debug logs around wrap() and unwrap() calls, as well + * as printing out the data sent/received in the I/O callbacks. * * @author wolfSSL */ @@ -63,9 +73,34 @@ public class WolfSSLEngine extends SSLEngine { /* closed completely (post shutdown or before handshake) */ private boolean closed = true; + /* handshake completed */ + private boolean handshakeFinished = false; + + /* closeNotify status when shutting down */ + private boolean closeNotifySent = false; + private boolean closeNotifyReceived = false; + + /* client/server mode has been set */ + private boolean clientModeSet = false; + static private SendCB sendCb = null; static private RecvCB recvCb = null; + /** Turn on extra/verbose SSLEngine debug logging */ + public boolean extraDebugEnabled = false; + + /** + * Turns on additional debugging based on system properties set. + */ + private void enableExtraDebug() { + /* turn on verbose extra debugging if 'wolfsslengine.debug' + * system property is set */ + String engineDebug = System.getProperty("wolfsslengine.debug"); + if ((engineDebug != null) && (engineDebug.equalsIgnoreCase("true"))) { + this.extraDebugEnabled = true; + } + } + /** * Create a new engine with no hints for session reuse * @@ -147,81 +182,180 @@ private void initSSL() throws WolfSSLException, WolfSSLJNIException { setCallbacks(); ssl.setIOReadCtx(this); ssl.setIOWriteCtx(this); + + enableExtraDebug(); } /** - * returns 0 if no data was waiting and size of copied on success - * negative values are returned in error cases + * Copy buffered data to be sent into provided output ByteBuffer. + * + * Data sent will be minimum of either buffered data size or + * destination buffer remaining space. + * + * Returns size of data copied. */ private int CopyOutPacket(ByteBuffer out, Status status) { - int max = 0; + int sendSz = 0; if (this.toSend != null) { - max = out.remaining(); - - if (this.toSend.length > max) { - /* output not large enough to read packet */ - status = Status.BUFFER_OVERFLOW; - return -1; + sendSz = Math.min(this.toSend.length, out.remaining()); + out.put(this.toSend, 0, sendSz); + + if (sendSz != this.toSend.length) { + /* resize and adjust remaining toSend data */ + byte[] tmp = new byte[this.toSend.length - sendSz]; + System.arraycopy(this.toSend, sendSz, tmp, 0, + this.toSend.length - sendSz); + this.toSend = tmp; } else { - /* read all from toSend */ - out.put(this.toSend); - max = this.toSend.length; this.toSend = null; } } - return max; + return sendSz; } /** - * Handles logic during shutdown + * Helper function, updates internal close_notify alert status + * and inBound/outBoundOpen. */ - private int ClosingConnection() { + private void UpdateCloseNotifyStatus() { int ret; - if (this.getUseClientMode() == true) { - EngineHelper.saveSession(); - } - ret = ssl.getShutdown(); if (ret == (WolfSSL.SSL_RECEIVED_SHUTDOWN | WolfSSL.SSL_SENT_SHUTDOWN)) { - /* sent and received close_notify alert, all finished */ + this.closeNotifySent = true; + this.closeNotifyReceived = true; + this.inBoundOpen = false; + this.outBoundOpen = false; closed = true; + } else if (ret == WolfSSL.SSL_RECEIVED_SHUTDOWN) { + this.closeNotifyReceived = true; this.inBoundOpen = false; + } else if (ret == WolfSSL.SSL_SENT_SHUTDOWN) { + this.closeNotifySent = true; this.outBoundOpen = false; - hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + } + } + + /** + * Handles logic during shutdown + */ + private int ClosingConnection() { + int ret; + + if (this.getUseClientMode() == true) { + EngineHelper.saveSession(); + } + + /* get current close_notify state */ + UpdateCloseNotifyStatus(); + if (this.closeNotifySent == true && + this.closeNotifyReceived == true) { return WolfSSL.SSL_SUCCESS; } + /* send/recv close_notify as needed */ ret = ssl.shutdownSSL(); - if (ret == WolfSSL.SSL_SUCCESS) { - /* if shutdown is successfull then is closed */ - closed = true; - this.inBoundOpen = false; - this.outBoundOpen = false; - hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + UpdateCloseNotifyStatus(); + + return ret; + } + + /** + * Starts or continues SSL/TLS handshake. + * Returns WolfSSL.SSL_SUCCESS or WolfSSL.SSL_FAILURE + */ + private int DoHandshake() throws SSLException { + int ret = WolfSSL.SSL_SUCCESS; + + try { + if (this.getUseClientMode() == true) { + ret = this.ssl.connect(); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ssl.connect() ret:err = " + ret + " : " + + ssl.getError(ret)); + } + else { + ret = this.ssl.accept(); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ssl.connect() ret:err = " + ret + " : " + + ssl.getError(ret)); + } + } catch (SocketTimeoutException e) { + throw new SSLException(e); } - /* wolfSSL_shutdown() will return either SSL_SHUTDOWN_NOT_DONE (2), or - * will map that to 0 if WOLFSSL_ERROR_CODE_OPENSSL is defined. Either - * should indicate that the full bidirectional shutdown has not - * completed. */ - else if (ret == WolfSSL.SSL_SHUTDOWN_NOT_DONE || ret == 0) { - hs = SSLEngineResult.HandshakeStatus.NEED_UNWRAP; + + return ret; + } + + /** + * Write application data using ssl.write(). + * + * Only sends up to maximum app data chunk size + * (SSLSession.getApplicationBufferSize()). + * + * Return bytes sent on success, negative on error */ + private int SendAppData(ByteBuffer[] in, int ofst, int len, + ByteBuffer out) throws SSLException { + + int i = 0; + int ret = 0; + int totalIn = 0; + int sendSz = 0; + int inputLeft = 0; + int inLimit = 0; + ByteBuffer dataBuf; + byte[] dataArr; + int pos[] = new int[len]; /* in[] positions */ + int limit[] = new int[len]; /* in[] limits */ + + /* get total input data size, store input array positions */ + for (i = ofst; i < ofst + len; i++) { + totalIn += in[i].remaining(); + pos[i] = in[i].position(); + limit[i] = in[i].limit(); } - else { - int err = ssl.getError(ret); - switch (err) { - case WolfSSL.SSL_ERROR_WANT_READ: - hs = SSLEngineResult.HandshakeStatus.NEED_UNWRAP; - break; - case WolfSSL.SSL_ERROR_WANT_WRITE: - hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; - break; - default: - } + + /* only send up to maximum app data size chunk */ + sendSz = Math.min(totalIn, + EngineHelper.getSession().getApplicationBufferSize()); + dataBuf = ByteBuffer.allocate(sendSz); + + /* gather byte array of sendSz bytes from input buffers */ + inputLeft = sendSz; + for (i = ofst; i < ofst + len; i++) { + int bufChunk = Math.min(in[i].remaining(), inputLeft); + + in[i].limit(in[i].position() + bufChunk); /* set limit */ + dataBuf.put(in[i]); /* get data */ + inputLeft -= bufChunk; + in[i].limit(limit[i]); /* reset limit */ + + if (inputLeft == 0) { + break; /* reached data size needed, stop reading */ + } } + + dataArr = new byte[sendSz]; + dataBuf.rewind(); + dataBuf.get(dataArr); + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "calling ssl.write() with size: " + sendSz); + + ret = this.ssl.write(dataArr, sendSz); + if (ret <= 0) { + /* error, reset in[] positions for next call */ + for (i = ofst; i < ofst + len; i++) { + in[i].position(pos[i]); + } + } + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ssl.write() returning: " + ret); + return ret; } @@ -235,10 +369,67 @@ public synchronized SSLEngineResult wrap(ByteBuffer in, ByteBuffer out) @Override public synchronized SSLEngineResult wrap(ByteBuffer[] in, int ofst, int len, ByteBuffer out) throws SSLException { - int i, max = 0, ret = 0, idx = 0, pro = 0; - ByteBuffer tmp; - byte[] msg; - int pos[] = new int[len]; + int ret = 0, i; + int produced = 0; + int consumed = 0; + + /* Set initial status for SSLEngineResult return */ + Status status = SSLEngineResult.Status.OK; + + if (in == null || ofst + len > in.length || out == null) { + throw new SSLException("SSLEngine.wrap() bad arguments"); + } + + if (this.clientModeSet == false) { + throw new IllegalStateException( + "setUseClientMode() has not been called on this SSLEngine"); + } + + if (extraDebugEnabled == true) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==== [ entering wrap() ] ==================================="); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "setUseClientMode: " + EngineHelper.getUseClientMode()); + for (i = 0; i < len; i++) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ByteBuffer in["+i+"].remaining(): " + in[i].remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ByteBuffer in["+i+"].position(): " + in[i].position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ByteBuffer in["+i+"].limit(): " + in[i].position()); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ofst: " + ofst + ", len: " + len); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out.remaining(): " + out.remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out.position(): " + out.position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out.limit(): " + out.limit()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toReadSz: " + this.toReadSz); + if (this.toSend != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: " + this.toSend.length); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: 0"); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifySent: " + this.closeNotifySent); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifyReceived: " + this.closeNotifyReceived); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "inBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "outBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "status: " + status); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "handshakeFinished: " + this.handshakeFinished); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==========================================================="); + } if (needInit) { EngineHelper.initHandshake(); @@ -246,76 +437,156 @@ public synchronized SSLEngineResult wrap(ByteBuffer[] in, int ofst, int len, closed = false; /* opened a connection */ } - /* for sslengineresults return */ - Status status = SSLEngineResult.Status.OK; - if (in == null || ofst + len > in.length || out == null) { - throw new SSLException("bad arguments"); + /* Force out buffer to be large enough to hold max packet size */ + if (out.remaining() < EngineHelper.getSession().getPacketBufferSize()) { + return new SSLEngineResult(Status.BUFFER_OVERFLOW, hs, 0, 0); } - /* check if left over data to be wrapped - * (pro can be negative on error) */ - pro = CopyOutPacket(out, status); + /* Copy buffered data to be sent into output buffer */ + produced = CopyOutPacket(out, status); /* check if closing down connection */ - if (pro >=0 && !outBoundOpen) { + if (produced >= 0 && !outBoundOpen) { status = SSLEngineResult.Status.CLOSED; ClosingConnection(); - ret = CopyOutPacket(out, status); - if (ret > 0) { - pro += ret; - } + produced += CopyOutPacket(out, status); } - else if (pro == 0) { - /* get buffer size */ - for (i = ofst; i < ofst + len; i++) { - max += in[i].remaining(); + else if (produced == 0) { + /* continue handshake or application data */ + if (this.handshakeFinished == false) { + ret = DoHandshake(); + } + else { + ret = SendAppData(in, ofst, len, out); + if (ret > 0) { + consumed += ret; } - tmp = ByteBuffer.allocate(max); + } - for (i = ofst; i < len; i++) { - pos[idx++] = in[i].position(); - tmp.put(in[i]); - } + /* copy any produced data into output buffer */ + produced += CopyOutPacket(out, status); + } - msg = new byte[max]; - tmp.rewind(); - tmp.get(msg); - ret = this.ssl.write(msg, max); - if (ret <= 0) { - int err = ssl.getError(ret); - - switch (err) { - case WolfSSL.SSL_ERROR_WANT_READ: - hs = SSLEngineResult.HandshakeStatus.NEED_UNWRAP; - break; - case WolfSSL.SSL_ERROR_WANT_WRITE: - hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; - break; - default: - throw new SSLException("wolfSSL error case " + ret); - } - } + SetHandshakeStatus(ret); - /* if the handshake is not done then reset input - * buffer postions */ - if (!ssl.handshakeDone()) { - idx = 0; - for (i = ofst; i < ofst + len; i++) { - in[i].position(pos[idx++]); - } + if (extraDebugEnabled == true) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==== [ exiting wrap() ] ==================================="); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "setUseClientMode: " + EngineHelper.getUseClientMode()); + for (i = 0; i < len; i++) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ByteBuffer in["+i+"].remaining(): " + in[i].remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ByteBuffer in["+i+"].position(): " + in[i].position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ByteBuffer in["+i+"].limit(): " + in[i].position()); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ofst: " + ofst + ", len: " + len); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out.remaining(): " + out.remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out.position(): " + out.position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out.limit(): " + out.limit()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toReadSz: " + this.toReadSz); + if (this.toSend != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: " + this.toSend.length); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: 0"); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifySent: " + this.closeNotifySent); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifyReceived: " + this.closeNotifyReceived); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "handshakeFinished: " + this.handshakeFinished); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "inBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "outBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "status: " + status); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "handshakeStatus: " + hs); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==========================================================="); + } + + return new SSLEngineResult(status, hs, consumed, produced); + } + + /** + * Receive application data using ssl.read() from in buffer, placing + * processed/decrypted data into out[]. + * + * @param in input data encrypted/encapsulated in SSL/TLS records + * @param out output ByteBuffer arrays, to hold processed/decoded plaintext + * @param ofst offset into out[] array to begin writing data + * @param length length of out[] array + * @param maxOutSz maximum size of all output buffers in out[] + * + * @return number of plaintext bytes received, or negative on error. + */ + private int RecvAppData(ByteBuffer in, ByteBuffer[] out, int ofst, + int length, int maxOutSz) throws SSLException { + + int i, sz, bufSpace; + int totalRead = 0; + int ret = 0; + int idx = 0; /* index into out[] array */ + byte[] tmp; + + /* read all data we have cached, if it fits in output buffers */ + while ((this.toReadSz > 0) && (totalRead < maxOutSz)) { + + tmp = new byte[maxOutSz]; + + ret = this.ssl.read(tmp, maxOutSz); + if (ret <= 0) { + int err = ssl.getError(ret); + + switch (err) { + case WolfSSL.SSL_ERROR_WANT_READ: + case WolfSSL.SSL_ERROR_WANT_WRITE: + break; + case WolfSSL.SSL_ERROR_ZERO_RETURN: + /* check if is shutdown message */ + if (ssl.getShutdown() == + WolfSSL.SSL_RECEIVED_SHUTDOWN) { + ret = ClosingConnection(); + return ret; + } + break; + default: + throw new SSLException("wolfSSL_read() error: " + + ret + " , err = " + err); } - else { - hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + } + else { + /* write processed data into output buffers */ + for (i = 0; i < ret;) { + if (idx + ofst >= length) { /* no more buffers left */ + break; + } + bufSpace = out[idx + ofst].remaining(); + sz = (bufSpace >= ret) ? ret : bufSpace; + out[idx + ofst].put(tmp, i, sz); + i += sz; + totalRead += sz; + + if (bufSpace < ret) { + idx++; /* go to next output buffer */ + } } - - pro = CopyOutPacket(out, status); + } } - /* consumed no bytes */ - if (ret < 0) { - ret = 0; - } - return new SSLEngineResult(status, hs, ret, pro); + return totalRead; } @Override @@ -327,21 +598,22 @@ public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer out) @Override public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer[] out, int ofst, int length) throws SSLException { - int i, ret = 0, sz = 0, idx = 0, max = 0, pos, cns = 0, pro = 0; + int i, ret = 0, sz = 0, idx = 0, maxOutSz = 0, pos; + int consumed = 0; + int produced = 0; byte[] tmp; - Status status; - if (needInit) { - EngineHelper.initHandshake(); - needInit = false; - closed = false; - } - - /* for sslengineresults return */ - status = SSLEngineResult.Status.OK; + /* Set initial status for SSLEngineResult return */ + Status status = SSLEngineResult.Status.OK; if (in == null || out == null || ofst + length > out.length) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "SSLEngine.unwrap() bad arguments"); + } + + if (this.clientModeSet == false) { + throw new IllegalStateException( + "setUseClientMode() has not been called on this SSLEngine"); } for (i = 0; i < length; i++) { @@ -349,78 +621,227 @@ public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer[] out, throw new IllegalArgumentException( "null or readonly out buffer found"); } - max += out[i + ofst].remaining(); + /* pre-calculate max output size, used in RecvAppData */ + maxOutSz += out[i + ofst].remaining(); } - sz = cns = in.remaining(); + if (extraDebugEnabled == true) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==== [ entering unwrap() ] ================================="); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "setUseClientMode: " + EngineHelper.getUseClientMode()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "in.remaining(): " + in.remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "in.position(): " + in.position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "in.limit(): " + in.position()); + for (i = 0; i < length; i++) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out["+i+"].remaining(): " + out[i].remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out["+i+"].position(): " + out[i].position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out["+i+"].limit(): " + out[i].limit()); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ofst: " + ofst + ", length: " + length); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toReadSz: " + this.toReadSz); + if (this.toSend != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: " + this.toSend.length); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: 0"); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifySent: " + this.closeNotifySent); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifyReceived: " + this.closeNotifyReceived); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "inBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "outBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "handshakeFinished: " + this.handshakeFinished); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "status: " + status); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==========================================================="); + } + + if (needInit) { + EngineHelper.initHandshake(); + needInit = false; + closed = false; + } + + sz = in.remaining(); pos = in.position(); if (sz > 0) { - /* add new encrypted input to the read buffer for - * wolfSSL_read call */ + /* add new encrypted input to read buffer for wolfSSL calls */ tmp = new byte[sz]; in.get(tmp); addToRead(tmp); + consumed += sz; + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "added " + sz + " bytes to internal read buffer"); } - tmp = new byte[max]; - if (!outBoundOpen) { + if (outBoundOpen == false) { if (ClosingConnection() == WolfSSL.SSL_SUCCESS) { status = SSLEngineResult.Status.CLOSED; } } else { - ret = this.ssl.read(tmp, max); - if (ret <= 0) { - int err = ssl.getError(ret); + if (this.handshakeFinished == false) { - switch (err) { - case WolfSSL.SSL_ERROR_WANT_READ: - if (cns > 0) { - hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; - } - break; - case WolfSSL.SSL_ERROR_WANT_WRITE: - break; + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "starting or continuing handshake"); + ret = DoHandshake(); + } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "receiving application data"); + ret = RecvAppData(in, out, ofst, length, maxOutSz); + if (ret > 0) { + consumed += ret; + } + } - case WolfSSL.SSL_ERROR_ZERO_RETURN: - /* check if is shutdown message */ - if (ssl.getShutdown() == WolfSSL.SSL_RECEIVED_SHUTDOWN) { - this.outBoundOpen = false; - ClosingConnection(); - status = SSLEngineResult.Status.CLOSED; - if (toSend != null && toSend.length > 0) { - hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; - } - } - break; + if (outBoundOpen == false) { + status = SSLEngineResult.Status.CLOSED; + } - default: - throw new SSLException("wolfSSL error case " + err); - } + int err = ssl.getError(ret); + if (ret < 0 && + (err != WolfSSL.SSL_ERROR_WANT_READ) && + (err != WolfSSL.SSL_ERROR_WANT_WRITE)) { + throw new SSLException( + "wolfSSL error, ret:err = " + ret + " : " + err); } } - for (i = 0; i < ret;) { - if (idx >= length) { /* no more output buffers left */ - break; + SetHandshakeStatus(ret); + + if (extraDebugEnabled == true) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==== [ exiting unwrap() ] =================================="); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "setUseClientMode: " + EngineHelper.getUseClientMode()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "in.remaining(): " + in.remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "in.position(): " + in.position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "in.limit(): " + in.position()); + for (i = 0; i < length; i++) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out["+i+"].remaining(): " + out[i].remaining()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out["+i+"].position(): " + out[i].position()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "out["+i+"].limit(): " + out[i].limit()); } - sz = out[idx + ofst].remaining(); - sz = (sz > ret)? ret : sz; - out[idx + ofst].put(tmp, i, sz); - i += sz; - pro += sz; - idx++; + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ofst: " + ofst + ", length: " + length); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toReadSz: " + this.toReadSz); + if (this.toSend != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: " + this.toSend.length); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "toSend.length: 0"); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "handshakeFinished: " + this.handshakeFinished); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifySent: " + this.closeNotifySent); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "closeNotifyReceived: " + this.closeNotifyReceived); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "inBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "outBoundOpen: " + this.outBoundOpen); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "handshakeStatus: " + hs); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "status: " + status); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "==========================================================="); } - if (ssl.handshakeDone()) { - hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + return new SSLEngineResult(status, hs, consumed, produced); + } + + /** + * Sets handshake status after I/O operation of unwrap(), helper function. + */ + private void SetHandshakeStatus(int ret) { + + int err = ssl.getError(ret); + + if (this.handshakeFinished == true) { + /* close_notify sent by wolfSSL but not across transport yet */ + if (this.closeNotifySent == true && + this.toSend != null && this.toSend.length > 0) { + hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; + } + /* close_notify received, need to send one back */ + else if (this.closeNotifyReceived == true && + this.closeNotifySent == false) { + hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; + } + /* close_notify sent, need to read peer's */ + else if (this.closeNotifySent == true && + this.closeNotifyReceived == false) { + hs = SSLEngineResult.HandshakeStatus.NEED_UNWRAP; + } + else { + hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + } } - return new SSLEngineResult(status, hs, cns, pro); + else { + if (ssl.handshakeDone() && this.toSend == null && + this.toReadSz == 0) { + this.handshakeFinished = true; + hs = SSLEngineResult.HandshakeStatus.FINISHED; + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "SSL/TLS handshake finished"); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "SSL/TLS protocol: " + + EngineHelper.getSession().getProtocol()); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "SSL/TLS cipher suite: " + + EngineHelper.getSession().getCipherSuite()); + } + /* give priority of WRAP/UNWRAP to state of our internal + * I/O data buffers first, then wolfSSL err status */ + else if (this.toSend != null && this.toSend.length > 0) { + hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; + } + else if (this.toReadSz > 0) { + hs = SSLEngineResult.HandshakeStatus.NEED_UNWRAP; + } + else if (ret < 0 && err == WolfSSL.SSL_ERROR_WANT_READ) { + hs = SSLEngineResult.HandshakeStatus.NEED_UNWRAP; + } + else if (ret < 0 && err == WolfSSL.SSL_ERROR_WANT_WRITE) { + hs = SSLEngineResult.HandshakeStatus.NEED_WRAP; + } + else { + hs = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + } + } + + return; } @Override public Runnable getDelegatedTask() { - /* no tasks left to run */ return null; } @@ -508,6 +929,7 @@ public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() { @Override public void setUseClientMode(boolean mode) { EngineHelper.setUseClientMode(mode); + this.clientModeSet = true; } @Override @@ -583,6 +1005,11 @@ protected synchronized int setOut(byte[] in, int sz) { } System.arraycopy(in, 0, tmp, idx, in.length); this.toSend = tmp; + + if (extraDebugEnabled == true) { + printHex(in, sz, "CB Write"); + } + return sz; } @@ -618,12 +1045,8 @@ protected synchronized int setIn(byte[] toRead, int sz) { this.toReadSz = 0; } - if (WolfSSLDebug.DEBUG) { - System.out.println("CB Read ["+max+"] :"); - for (int i = 0; i < max; i++) { - System.out.printf("%02X", toRead[i]); - } - System.out.println(""); + if (extraDebugEnabled == true) { + printHex(toRead, max, "CB Read"); } return max; @@ -642,6 +1065,46 @@ private synchronized void addToRead(byte[] in) { toReadSz += in.length; } + /** + * Helper function, print byte[] as hex to stdout + * + * @param in input array to be printed as hex + * @param sz number of bytes to print from input array + * @param label label String to previx output with + */ + protected void printHex(byte[] in, int sz, String label) { + int i = 0, j = 0; + + System.out.print(label + " [" + sz + "]: "); + for (i = 0; i < sz; i++) { + if ((i % 8) == 0) { + System.out.printf("\n%06X", j * 8); + j++; + } + System.out.printf(" %02X ", in[i]); + } + System.out.println(""); + } + + /** + * Removes the last 'sz' bytes from the internal read buffer. + * Used to back out application data not yet ready for processing. + */ + private synchronized void removeFromRead(int sz) { + byte[] reduced; + + if (sz > toReadSz || toRead == null || sz == 0) { + return; + } + + reduced = new byte[toReadSz - sz]; + System.arraycopy(toRead, 0, reduced, 0, toReadSz - sz); + toRead = reduced; + toReadSz = toReadSz - sz; + + return; + } + private class SendCB implements WolfSSLIOSendCallback { protected SendCB() { diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java b/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java index 2b754a51..e15d6022 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java @@ -488,19 +488,21 @@ public int getPeerPort() { @Override public int getPacketBufferSize() { - return 16394; /* 2^14, max size by standard, enum MAX_RECORD_SIZE */ + /* Match conscrypt's calculations here for maximum potential + * SSL/TLS record length. Used by SSLEngine consumers to allocate + * output buffer size. + * + * type(1) + version(2) + length(2) + 2^14 plaintext + + * max compression overhead (1024) + max AEAD overhead (1024) */ + return 18437; } @Override public int getApplicationBufferSize() { - /* 16394 - (38 + 64) - * max added to msg, mac + pad from RECORD_HEADER_SZ + BLOCK_SZ (pad) + - * Max digest sz + BLOC_SZ (iv) + pad byte (1) - */ - return 16292; + /* max plaintext bytes allowed by spec, MAX_RECORD_SIZE enum (2^14) */ + return 16384; } - /** * Takes in a new WOLFSSL object and sets the stored session * @param in WOLFSSL session to set resume in diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java index 8be9ab43..0530e29b 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java @@ -355,6 +355,76 @@ public void testConnectionOutIn() pass("\t\t... passed"); } + @Test + public void testSetUseClientMode() + throws NoSuchProviderException, NoSuchAlgorithmException { + + int ret; + SSLEngine client; + SSLEngine server; + + System.out.print("\tTesting setUseClientMode()"); + + /* expected to fail, not calling setUseClientMode() */ + this.ctx = tf.createSSLContext("TLS", engineProvider); + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + try { + ret = tf.testConnection(server, client, null, null, "Testing"); + error("\t... failed"); + fail("did not fail without setUseClientMode()"); + } catch (IllegalStateException e) { + /* expected */ + } + + /* expected to fail, only calling client.setUseClientMode() */ + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); + try { + ret = tf.testConnection(server, client, null, null, "Testing"); + error("\t... failed"); + fail("did not fail without server.setUseClientMode()"); + } catch (IllegalStateException e) { + /* expected */ + } + + /* expected to fail, only calling client.setUseClientMode() */ + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + server.setUseClientMode(false); + try { + ret = tf.testConnection(server, client, null, null, "Testing"); + error("\t... failed"); + fail("did not fail without client.setUseClientMode()"); + } catch (IllegalStateException e) { + /* expected */ + } + + /* expected to succeed, both setUseClientMode() set */ + server = this.ctx.createSSLEngine(); + client = this.ctx.createSSLEngine("wolfSSL test", 11111); + server.setWantClientAuth(false); + server.setNeedClientAuth(false); + client.setUseClientMode(true); + server.setUseClientMode(false); + try { + ret = tf.testConnection(server, client, null, null, "Testing"); + } catch (IllegalStateException e) { + e.printStackTrace(); + error("\t... failed"); + fail("failed with setUseClientMode(), should succeed"); + } + + pass("\t... passed"); + } + @Test public void testMutualAuth() throws NoSuchProviderException, NoSuchAlgorithmException { @@ -396,6 +466,7 @@ public void testMutualAuth() server.setWantClientAuth(true); server.setNeedClientAuth(true); client.setUseClientMode(true); + server.setUseClientMode(false); ret = tf.testConnection(server, client, null, null, "Test in/out bound"); if (ret == 0) { error("\t\t... failed"); diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java index 7ed29bba..85307e39 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java @@ -612,7 +612,10 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos if (extraDebug) { System.out.println("client status = " + s.toString()); } - if (result.bytesProduced() <= 0 || result.bytesConsumed() == 0) { + /* result.bytesProduced() should be > 0 for closeNotify produced. + * consumed will be 0 at this point since peer closeNotify not yet + * consumed */ + if (result.bytesProduced() <= 0) { throw new SSLException("Client wrap consumed/produced error"); } if (!s.toString().equals("NEED_UNWRAP") || @@ -647,6 +650,7 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos return 0; } + /* server unwraps client close_notify */ result = server.unwrap(cliToSer, empty); if (extraDebug) { System.out.println("[server unwrap] consumed = " + result.bytesConsumed() + @@ -669,6 +673,7 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos throw new SSLException("Bad status"); } + /* server wraps its own close_notify */ result = server.wrap(empty, serToCli); if (extraDebug) { System.out.println("[server wrap] consumed = " + result.bytesConsumed() + @@ -690,6 +695,7 @@ public int CloseConnection(SSLEngine server, SSLEngine client, boolean earlyClos throw new SSLException("Bad status"); } + /* client unwraps server close_notify */ serToCli.flip(); result = client.unwrap(serToCli, empty); if (extraDebug) { From 10428bb8cc5d8f5b9fb9a75ebd67fef52ecc4ed2 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Tue, 9 Aug 2022 14:13:24 -0600 Subject: [PATCH 2/2] SSLEngine: add tests for getApplication/PacketBufferSize(), large 16k data transfer --- .../provider/jsse/test/WolfSSLEngineTest.java | 279 ++++++++++++++---- 1 file changed, 215 insertions(+), 64 deletions(-) diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java index 0530e29b..648af10c 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java @@ -32,10 +32,12 @@ import java.security.Security; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.util.Random; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -57,9 +59,10 @@ public class WolfSSLEngineTest { private SSLContext ctx = null; private static String allProtocols[] = { - "TLSV1", - "TLSV1.1", - "TLSV1.2", + "TLSv1", + "TLSv1.1", + "TLSv1.2", + "TLSv1.3", "TLS" }; @@ -70,6 +73,8 @@ public class WolfSSLEngineTest { public static void testProviderInstallationAtRuntime() throws NoSuchProviderException { + SSLContext ctx; + System.out.println("WolfSSLEngine Class"); /* install wolfJSSE provider at runtime */ @@ -78,6 +83,17 @@ public static void testProviderInstallationAtRuntime() Provider p = Security.getProvider("wolfJSSE"); assertNotNull(p); + /* populate enabledProtocols */ + for (int i = 0; i < allProtocols.length; i++) { + try { + ctx = SSLContext.getInstance(allProtocols[i], "wolfJSSE"); + enabledProtocols.add(allProtocols[i]); + + } catch (NoSuchAlgorithmException e) { + /* protocol not enabled */ + } + } + try { tf = new WolfSSLTestFactory(); } catch (WolfSSLException e) { @@ -95,11 +111,14 @@ public void testSSLEngine() /* create new SSLEngine */ System.out.print("\tTesting creation"); - this.ctx = tf.createSSLContext("TLSv1.2", engineProvider); - e = this.ctx.createSSLEngine(); - if (e == null) { - error("\t\t... failed"); - fail("failed to create engine"); + for (int i = 0; i < enabledProtocols.size(); i++) { + this.ctx = tf.createSSLContext(enabledProtocols.get(i), + engineProvider); + e = this.ctx.createSSLEngine(); + if (e == null) { + error("\t\t... failed"); + fail("failed to create engine for " + enabledProtocols.get(i)); + } } pass("\t\t... passed"); } @@ -177,18 +196,6 @@ public void testCipherConnection() server = this.ctx.createSSLEngine(); client = this.ctx.createSSLEngine("wolfSSL client test", 11111); -// /* use wolfJSSE client */ -// SSLContext c = SSLContext.getInstance("TLS", "wolfJSSE"); -// try { -// c.init(createKeyManager("SunX509", clientJKS), -// createTrustManager("SunX509", clientJKS), null); -// } catch (KeyManagementException ex) { -// Logger.getLogger(WolfSSLEngineTest.class.getName()).log(Level.SEVERE, null, ex); -// } -// client = c.createSSLEngine("wolfSSL client test", 11111); -// server = c.createSSLEngine(); - - ciphers = client.getSupportedCipherSuites(); certs = server.getSession().getLocalCertificates(); if (certs != null) { @@ -490,16 +497,6 @@ public void testReuseSession() server = this.ctx.createSSLEngine(); client = this.ctx.createSSLEngine("wolfSSL client test", 11111); - /* use wolfJSSE client */ -// SSLContext c = SSLContext.getInstance("TLSv1.2", "wolfJSSE"); -// try { -// c.init(createKeyManager("SunX509", clientJKS), -// createTrustManager("SunX509", clientJKS), null); -// } catch (KeyManagementException ex) { -// Logger.getLogger(WolfSSLEngineTest.class.getName()).log(Level.SEVERE, null, ex); -// } -// client = c.createSSLEngine("wolfSSL client test", 11111); -// server = c.createSSLEngine(); server.setUseClientMode(false); server.setNeedClientAuth(false); client.setUseClientMode(true); @@ -517,18 +514,6 @@ public void testReuseSession() fail("failed to create engine"); } - /* use wolfJSSE client */ -// c = SSLContext.getInstance("TLSv1.2", "wolfJSSE"); -// try { -// c.init(createKeyManager("SunX509", clientJKS), -// createTrustManager("SunX509", clientJKS), null); -// } catch (KeyManagementException ex) { -// Logger.getLogger(WolfSSLEngineTest.class.getName()).log(Level.SEVERE, null, ex); -// } -// client = c.createSSLEngine("wolfSSL client test", 11111); -// server = c.createSSLEngine(); - - server = this.ctx.createSSLEngine(); client = this.ctx.createSSLEngine("wolfSSL client test", 11111); client.setEnableSessionCreation(false); @@ -568,17 +553,6 @@ public void testThreadedUse() server = new ServerEngine(this); client = new ClientEngine(this); - /* use wolfJSSE client */ -// SSLContext c = SSLContext.getInstance("TLSv1.2", "wolfJSSE"); -// try { -// c.init(createKeyManager("SunX509", clientJKS), -// createTrustManager("SunX509", clientJKS), null); -// } catch (KeyManagementException ex) { -// Logger.getLogger(WolfSSLEngineTest.class.getName()).log(Level.SEVERE, null, ex); -// } -// client = c.createSSLEngine("wolfSSL client test", 11111); -// server = c.createSSLEngine(); - client.setServer(server); server.setClient(client); @@ -590,7 +564,9 @@ public void testThreadedUse() client.join(1000); } catch (InterruptedException ex) { System.out.println("interupt happened"); - Logger.getLogger(WolfSSLEngineTest.class.getName()).log(Level.SEVERE, null, ex); + Logger.getLogger( + WolfSSLEngineTest.class.getName()).log( + Level.SEVERE, null, ex); } if (!server.success || !client.success) { @@ -629,7 +605,8 @@ public ServerEngine(WolfSSLEngineTest in) { @Override public void run() { ByteBuffer out = - ByteBuffer.allocateDirect(server.getSession().getPacketBufferSize());; + ByteBuffer.allocateDirect( + server.getSession().getPacketBufferSize());; ByteBuffer in = ByteBuffer.wrap("Hello wolfSSL JSSE".getBytes()); do { @@ -649,7 +626,8 @@ public void run() { } status = result.getHandshakeStatus(); } catch (SSLException ex) { - Logger.getLogger(WolfSSLEngineTest.class.getName()).log(Level.SEVERE, null, ex); + Logger.getLogger(WolfSSLEngineTest.class.getName()).log( + Level.SEVERE, null, ex); return; } } while (status != HandshakeStatus.NOT_HANDSHAKING); @@ -661,8 +639,8 @@ public void run() { protected void toServer(ByteBuffer in) throws SSLException { Runnable run; SSLEngineResult result; - ByteBuffer out = - ByteBuffer.allocateDirect(server.getSession().getPacketBufferSize());; + ByteBuffer out = ByteBuffer.allocateDirect( + server.getSession().getPacketBufferSize());; result = server.unwrap(in, out); while ((run = server.getDelegatedTask()) != null) { run.run(); @@ -682,7 +660,8 @@ protected class ClientEngine extends Thread protected boolean success; public ClientEngine(WolfSSLEngineTest in) { - client = in.ctx.createSSLEngine("wolfSSL threaded client test", 11111); + client = in.ctx.createSSLEngine("wolfSSL threaded client test", + 11111); client.setUseClientMode(true); status = HandshakeStatus.NOT_HANDSHAKING; success = false; @@ -690,8 +669,8 @@ public ClientEngine(WolfSSLEngineTest in) { @Override public void run() { - ByteBuffer out = - ByteBuffer.allocateDirect(client.getSession().getPacketBufferSize());; + ByteBuffer out = ByteBuffer.allocateDirect( + client.getSession().getPacketBufferSize());; ByteBuffer in = ByteBuffer.wrap("Hello wolfSSL JSSE".getBytes()); do { @@ -711,7 +690,8 @@ public void run() { } status = result.getHandshakeStatus(); } catch (SSLException ex) { - Logger.getLogger(WolfSSLEngineTest.class.getName()).log(Level.SEVERE, null, ex); + Logger.getLogger(WolfSSLEngineTest.class.getName()).log( + Level.SEVERE, null, ex); return; } } while (status != HandshakeStatus.NOT_HANDSHAKING); @@ -721,8 +701,8 @@ public void run() { protected void toClient(ByteBuffer in) throws SSLException { Runnable run; SSLEngineResult result; - ByteBuffer out = - ByteBuffer.allocateDirect(client.getSession().getPacketBufferSize()); + ByteBuffer out = ByteBuffer.allocateDirect( + client.getSession().getPacketBufferSize()); result = client.unwrap(in, out); while ((run = client.getDelegatedTask()) != null) { run.run(); @@ -733,4 +713,175 @@ protected void setServer(ServerEngine in) { server = in; } } + + @Test + public void testGetApplicationBufferSize() { + + int appBufSz = 0; + SSLEngine engine; + SSLSession session; + + System.out.print("\tTesting getAppBufferSize"); + + try { + /* create SSLContext */ + this.ctx = tf.createSSLContext("TLS", engineProvider); + + engine = this.ctx.createSSLEngine("test", 11111); + session = engine.getSession(); + appBufSz = session.getApplicationBufferSize(); + + /* expected to be 16384 */ + if (appBufSz != 16384) { + error("\t... failed"); + fail("got incorrect application buffer size"); + } + } catch (Exception e) { + e.printStackTrace(); + error("\t... failed"); + fail("unexpected Exception during getApplicationBufferSize test"); + } + + pass("\t... passed"); + } + + @Test + public void testGetPacketBufferSize() { + + int packetBufSz = 0; + SSLEngine engine; + SSLSession session; + + System.out.print("\tTesting getPacketBufferSize"); + + try { + /* create SSLContext */ + this.ctx = tf.createSSLContext("TLS", engineProvider); + + engine = this.ctx.createSSLEngine("test", 11111); + session = engine.getSession(); + packetBufSz = session.getPacketBufferSize(); + + /* expected to be 18437 */ + if (packetBufSz != 18437) { + error("\t... failed"); + fail("got incorrect packet buffer size"); + } + } catch (Exception e) { + e.printStackTrace(); + error("\t... failed"); + fail("unexpected Exception during getPacketBufferSize test"); + } + + pass("\t... passed"); + } + + @Test + public void testSSLEngineBigInput() throws Exception { + + int appBufMax, netBufMax; + int done = 0; + ByteBuffer cIn; + ByteBuffer cOut; + ByteBuffer sIn; + ByteBuffer sOut; + ByteBuffer clientToServer; + ByteBuffer serverToClient; + + /* big input buffer to test, 16k */ + byte[] bigInput = new byte[16384]; + + SSLEngineResult cResult; + SSLEngineResult sResult; + + System.out.print("\tTesting large data transfer"); + + try { + /* create SSLContext */ + this.ctx = tf.createSSLContext("TLS", engineProvider); + + /* create server SSLEngine */ + SSLEngine server = this.ctx.createSSLEngine(); + server.setUseClientMode(false); + server.setNeedClientAuth(true); + + /* create client SSLEngine */ + SSLEngine client = this.ctx.createSSLEngine( + "wolfSSL client test", 11111); + client.setUseClientMode(true); + + SSLSession session = client.getSession(); + appBufMax = session.getApplicationBufferSize(); + netBufMax = session.getPacketBufferSize(); + + cIn = ByteBuffer.allocate(appBufMax); + sIn = ByteBuffer.allocate(netBufMax); + clientToServer = ByteBuffer.allocate(netBufMax); + serverToClient = ByteBuffer.allocate(netBufMax); + + /* generate random bytes for input buffer */ + Random rand = new Random(); + rand.nextBytes(bigInput); + + cOut = ByteBuffer.wrap(bigInput); + sOut = ByteBuffer.wrap("Hello client, from server".getBytes()); + + while (!(client.isOutboundDone() && client.isInboundDone()) && + !(server.isOutboundDone() && server.isInboundDone())) { + + cResult = client.wrap(cOut, clientToServer); + sResult = server.wrap(sOut, serverToClient); + + clientToServer.flip(); + serverToClient.flip(); + + cResult = client.unwrap(serverToClient, cIn); + sResult = server.unwrap(clientToServer, sIn); + + clientToServer.compact(); + serverToClient.compact(); + + if (done == 0 && + (cOut.limit() == sIn.position()) && + (sOut.limit() == cIn.position())) { + + /* check server out matches client in */ + sOut.flip(); + cIn.flip(); + + if (!sOut.equals(cIn)) { + error("\t... failed"); + fail("server output does not match client input"); + } + sOut.position(sOut.limit()); + cIn.position(cIn.limit()); + sOut.limit(sOut.capacity()); + cIn.limit(cIn.capacity()); + + /* check client out matches server in */ + cOut.flip(); + sIn.flip(); + + if (!cOut.equals(sIn)) { + error("\t... failed"); + fail("client output does not match server input"); + } + cOut.position(cOut.limit()); + sIn.position(sIn.limit()); + cOut.limit(cOut.capacity()); + sIn.limit(sIn.capacity()); + + /* close client outbound, mark done */ + client.closeOutbound(); + done = 1; + } + } + } catch (Exception e) { + error("\t... failed"); + e.printStackTrace(); + fail("failed large input test with Exception"); + } + pass("\t... passed"); + } } +