1
votes

I have a socket application that reads and writes data. I'm using OpenSSL to do the encryption/decryption. My question is whether the "BIO_write" method can buffer data internally or if I have to append to a growing buffer as I read more from the socket. Here's what I'm doing.

I read from the socket and I use the class method below to write all the bytes that were read into the BIO:

int CSslConnectionContext::Store(BYTE* pbData, DWORD dwDataLength)
{
    int bytes = 0;
    if (dwDataLength > 0)
    {
        bytes = BIO_write(bio[BIO_RECV], pbData, dwDataLength);
    }

    return bytes;
}

I then immediately call the SSL_read method to get decrypted data:

int CSslConnectionContext::Read(BYTE* pbBuffer, DWORD dwBufferSize)
{
    int bytes = SSL_read(ssl, pbBuffer, dwBufferSize);
    return bytes;
}

If SSL_read returns a positive number then I've got decrypted data available for my application.

What I'm not sure of is what happens when my socket read doesn't capture all of the data required for decryption in a single read.

So if I need 100 bytes to be able to decrypted the data and the first read only gets 80, can I call BIO_write() with those 80, do another socket read to get the next 20, and then call BIO_write() with just those 20 bytes?

Or do I need to write my code so when I read 80 I do something like this:

  1. call BIO_write() with the 80 bytes.
  2. if that returns a failure indicator - hold onto that 80 bytes.
  3. read the next 20 bytes from the socket and append it to the buffered 80 bytes.
  4. call BIO_write() with 100 bytes
2
The man page for SSL_read() seems to handle the case where more data is needed in the underlying bio before SSL_read can return any data at all. In that case, SSL_read() will return a negative number, and then SSL_get_error() will return a value corresponding to SSL_WANT_READ or SSL_WANT_WRITE. - President James K. Polk

2 Answers

2
votes

OpenSSL holds internal buffer - let's call it SSL stack - on top of TCP stack. OpenSSL library handles SSL stack. BIO_xxx() functions can operate on different end-points: i.e. memory, sockets. It behaves differently depending on the actual item it operates on. For instance if BIO_write() uses memory (BIO_s_mem), BIO_write never fails except insufficient memory. But if it uses socket, and socket is non-blocking it can return error on failure, or it can write some number of bytes instead of all of the requested bytes where socket buffer is full.

So, how to use/handle buffer depends many factors, but most noticable ones are:

  • Blocking or Nonblocking IO
  • BIO object that operates on (memory, socket, etc.)

For instance if you're using BIO_s_mem and non-blocking socket operations, following technique can be applied:

  1. Write buffer using BIO_write, and check if it failed. If it did not fail, you can be sure that you've written all buffer to SSL stack.
  2. Call Read_SSL and check for errors, if error is WANT_READ, or WANT_WRITE then you need to write more data to SSL stack to be able to read a valid record.

For the question and example:

You can write partially (As many as you can, even 1 byte). For instance if you read 80 bytes from socket, then write those using BIO_write. Then call to SSL_read may fail (WANT_READ, WANT_WRITE, or other). Then you receive 20 bytes from socket, then write these bytes using BIO_write. Then call SSL_read again. Whenever SSL_read returns without error this means SSL stack decoded a valid record.

But it is quite important to understand waiting on non-blocking sockets using select() to handle SSL reads/writes are quite cumbersome. One SSL_write can result multiple writes to socket while you already waiting for READ event for the socket.

0
votes

please use bio_pending.. to know all the bytes available with openssl.. Loop using the return value of bio_pending. This should be called before bio_read.