can't pass data during rehandshake?

In the docs I read

"New handshaking data can be intermixed among the application data"

I can't seem to get that too work. Below is alot of code all of which works except the very last line. There are only two logs in this file and I paste those after the code too. Notice SSLEngine for some reason doesn't even read my encrypted data from the buffer while in handshake. What is gonig on? what do I have wrong?

MockSSLEngineFactory factory =new MockSSLEngineFactory();

SSLEngine server = factory.createEngineForServerSocket();

SSLEngine client = factory.createEngineForSocket();

client.beginHandshake();

SSLSession s = client.getSession();

ByteBuffer unencrPacket = ByteBuffer.allocate(s.getApplicationBufferSize());

ByteBuffer encPacket = ByteBuffer.allocate(s.getPacketBufferSize());

encPacket.clear();

unencrPacket.clear();

SSLEngineResult result = client.wrap(unencrPacket, encPacket);

assertEquals(result.getHandshakeStatus(), HandshakeStatus.NEED_UNWRAP);

assertEquals(result.getStatus(), Status.OK);

helper.doneFillingBuffer(encPacket);

result = server.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.NEED_TASK, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

Runnable r = server.getDelegatedTask();

r.run();

assertEquals(HandshakeStatus.NEED_WRAP, server.getHandshakeStatus());

encPacket.clear();

result = server.wrap(unencrPacket, encPacket);

assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

helper.doneFillingBuffer(encPacket);

result = client.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.NEED_TASK, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

r = client.getDelegatedTask();

r.run();

assertEquals(HandshakeStatus.NEED_WRAP, client.getHandshakeStatus());

encPacket.clear();

result = client.wrap(unencrPacket, encPacket);

assertEquals(HandshakeStatus.NEED_WRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

helper.doneFillingBuffer(encPacket);

result = server.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.NEED_TASK, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

r = server.getDelegatedTask();

r.run();

assertEquals(HandshakeStatus.NEED_UNWRAP, server.getHandshakeStatus());

encPacket.clear();

result = client.wrap(unencrPacket, encPacket);

assertEquals(HandshakeStatus.NEED_WRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

helper.doneFillingBuffer(encPacket);

result = server.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

encPacket.clear();

result = client.wrap(unencrPacket, encPacket);

assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

helper.doneFillingBuffer(encPacket);

result = server.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.NEED_WRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

encPacket.clear();

result = server.wrap(unencrPacket, encPacket);

assertEquals(HandshakeStatus.NEED_WRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

helper.doneFillingBuffer(encPacket);

result = client.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

encPacket.clear();

result = server.wrap(unencrPacket, encPacket);

assertEquals(HandshakeStatus.FINISHED, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

helper.doneFillingBuffer(encPacket);

result = client.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.FINISHED, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

/*****************************************************

* REHANDSHAKE BEGINS HERE.............

*****************************************************/

client.beginHandshake();

assertEquals(HandshakeStatus.NEED_WRAP, client.getHandshakeStatus());

encPacket.clear();

result = client.wrap(unencrPacket, encPacket);//first handshake message

assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

String expected ="abc";

ByteBuffer encData = ByteBuffer.allocate(s.getPacketBufferSize());

ByteBuffer data = ByteBuffer.allocate(10);

helper.putString(data, expected);

helper.doneFillingBuffer(data);

result = client.wrap(data, encData);

assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

unencrPacket.clear();

helper.doneFillingBuffer(encPacket);

result = server.unwrap(encPacket, unencrPacket);

assertEquals(HandshakeStatus.NEED_TASK, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

r = server.getDelegatedTask();//get task but don't run it yet...wait until after decrypt of real data

ByteBuffer dataOut = ByteBuffer.allocate(server.getSession().getApplicationBufferSize());

dataOut.clear();

helper.doneFillingBuffer(encData);

log.fine("datain1="+encData+" out="+dataOut);

result = server.unwrap(encData, dataOut);

log.fine("datain2="+encData+" out="+dataOut);

assertEquals(HandshakeStatus.NEED_TASK, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

helper.doneFillingBuffer(dataOut);

String actual = helper.readString(dataOut, dataOut.remaining());

assertEquals(expected, actual);

FINE: datain1=java.nio.HeapByteBuffer[pos=0 lim=37 cap=16665] out=java.nio.HeapByteBuffer[pos=0 lim=16384 cap=16384]

Jul 4, 2005 3:45:50 PM biz.xsoftware.impl.nio.secure.test.TestNewAsynchSSLEngine testRawSSLEngine

FINE: datain2=java.nio.HeapByteBuffer[pos=0 lim=37 cap=16665] out=java.nio.HeapByteBuffer[pos=0 lim=16384 cap=16384]

[6772 byte] By [deanhiller2000a] at [2007-10-1 17:45:29]
# 1

Dean, what the OP describes is indeed possible but you are never going to get it to work this way, i.e. by making assumptions about what the engine status must be at each step instead of asking it and responding accordingly. The handshake is complex enough and has enough options that you need to go to a state machine model. How are you going to cope with an incoming rehandshake? remembering that there are two kinds?

ejpa at 2007-7-11 12:12:25 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 2

String expected = "abc";

ByteBuffer encData = ByteBuffer.allocate(s.getPacketBufferSize());

ByteBuffer data = ByteBuffer.allocate(10);

helper.putString(data, expected);

helper.doneFillingBuffer(data);

result = client.wrap(data, encData);

assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());

assertEquals(Status.OK, result.getStatus());

Does the SSLEngine read from data at this point, or does it just send handshake data.

All of my work with using SSLEngine has not allowed any writes during handshaking, so I am not sure if it supports that. However I HAVE read from SSLEngine during handshaking, which is why my doHandshake method requires a ByteBuffer to put data into. In the tests, the SSLEngine was the server, while the client was a simple SSLSocket in another VM on the machine.

Talchasa at 2007-7-11 12:12:25 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 3
If the handshake status is OK you can wrap & send application data. If it is NEED_WRAP the engine wants to wrap handshake data, after which you have to write it.
ejpa at 2007-7-11 12:12:25 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 4

ejp, don't worry...the code above is just my reproduction of the problem in the simplest form....yes my statemachine does handle various things but for some reason does not handle the above....I wrote that JUnit test just to prove that SSLEngine doesn't seem able to process data during a handshake which could cause backups in data that is being exchanged between client and server. This kind of concerns me as the statement in the docs was

"New handshaking data can be intermixed among the application data"

What is wrong with my code?(besides that it doesn't handle all the other things...again realize this is not my real code, just a reproductino of the problem i am encountering).

thanks for any help with it,

dean

deanhiller2000a at 2007-7-11 12:12:25 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 5

ps. I am confused your statement

"If the handshake status is OK you can wrap & send application data"

my status IS ok as it is a rehandshake. HandshakeStatus is NEED_WRAP but according to docs, I should be able to intermingle data and handshake data.

doc states......

"New handshaking data can be intermixed among the application data"

What is wrong with my code above so I can apply it to my real code....ps...I can post my real code instead...it is much more complex though.....all the code above passes!!! except for the last statement.

deanhiller2000a at 2007-7-11 12:12:25 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 6

Just wrap when you want to wrap or when the handshake status tells you to. In either case you have to provide a source buffer and it may as well have the data you want to send in it. What order the SSLEngine sends in in w.r.t. the handshake is up to it, you can't control it.

PS I don't work for Sun but (a) I am about to publish a book with a long chapter on the SSLEngine and (b) I am about to release a Scalable SSL for Java product which provides SSLSelectors, SSLSocketChannels and SSLServerSocketChannels. Beta testers welcome. See http://www.telekinesis.com.au, Products tag.

EJP

ejpa at 2007-7-11 12:12:25 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 7

important...please read my whole response below as I really think this is a bug after some further investigation and it takes me a while to explain!!!!!

so in that test, isn't it a bug that the last call to unwrap fails.....

result = server.unwrap(encData, dataOut);

This results in encdata not being read at all and nothing being put in dataOut....Is this an SSLEngine bug?

Now, if I change the task to actually run the task before I make that call to unwrap, my test will then succeed and data can be intermingled with handshake....ie. this forces the Runnable to have to be executed inline or to have to buffer incoming data and let it backup until the Runnable completes. I really think this is a bug. What are your thoughts ejp?

ps. let me know when that book comes out and thanks for the links!!!!!!

thanks,

dean

deanhiller2000a at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 8
Are you handling BUFFER_OVERFLOW after a wrap? it means 'write and clear the buffer'.Similarly BUFFER_UNDERFLOW after an unwrap means 'read more data into the buffer'.
ejpa at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 9

answer to your question at the end. First I wanted to explain the code above as I think I may not have been as clear as I would have liked from your question......

Please look real close at the code above....notice that after my last call to unwrap in the code above, I assert that Status==OK and HandshakeStatus==NEED_TASK....ie. there was no BufferOverflow or Underflow in the code above.

Now, look at my two logs.....which clearly show SSLEngine didn't even touch the Buffers as usually the second log shows that SSLEngine at least read from the input buffer.......

FINE: datain1=java.nio.HeapByteBuffer[pos=0 lim=37 cap=16665] out=java.nio.HeapByteBuffer[pos=0 lim=16384 cap=16384]

Jul 4, 2005 3:45:50 PM biz.xsoftware.impl.nio.secure.test.TestNewAsynchSSLEngine testRawSSLEngine

FINE: datain2=java.nio.HeapByteBuffer[pos=0 lim=37 cap=16665] out=java.nio.HeapByteBuffer[pos=0 lim=16384 cap=16384]

Am I making sense.....I don't always explain the problem as clearly as I would like.

now my real code, I handle Buffer underflow. Buffer overflow...I am not sure this is possible as I clear the out buffer right before unwrapping in my real code, but alas, I do handle it by throwing an exception if I ever get a BufferOverflow and have never gotten one!

My e-mail is dean@xsoftware.biz. If you send me a mail, i can send you the real code and all my unit tests, but the code above does clearly show the bug too. After all, this code is going to be put in open source anyways.

thanks,

dean

deanhiller2000a at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 10
I am not going to read your code unless you pay me, sorry. As the documentation implies and as I have already described, BUFFER_OVERFLOW is possible on both wrap and unwrap, and the correct reaction is different in each case.
ejpa at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 11
as seen from the assert statements, the status is not Buffer Overflow nor Buffer underflow.thanks for your help anyways though.dean
deanhiller2000a at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 12
As the handshake status is NEED_TASK you need to run the task. See your other thread.
ejpa at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 13
Dean, I have finally twigged that you are talking about a re-handshake, in which case I agree it is a bug. The old session should still be usable until the final change_cipher_spec message, and the engine should not get itself stuck in an intermediate state.
ejpa at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 14

thanks much, sorry, yeah, I see how that could have been missed. Everything else with SSLEngine worked fine(I have about 11 unit tests testing every corner case I could think of). Just this one seems weird as I have to currently block under while Runnables get run. The other intermingling seems to work fine after the Runnable is run.

thanks,

dean

deanhiller2000a at 2007-7-11 12:12:26 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 15
for anyone else that reads this post. It was discovered from sun this is not a bug and sun is thinking about changing their docs to be a little more clear that a Runnable must be run before app data can be passed to unwrapthanks,dean
deanhiller2000a at 2007-7-20 9:49:39 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...