Continue from last week problem, curl client shows “Empty reply from server” just because I did not send a proper HTTP response which starts with the message status line (something like “HTTP/1.1 200 OK”) thus the client just ignores the packages. Simply br_sslio_write() the status line at the start of the response then it works perfectly fine.

Then I start to replicate what were done before with wolfSSL,  grab the HTTP request line and pass the request type to the Cross-Origin Resource Sharing (CORS) state machine (basically it requires an OPTION request to be sent before processing any other request, otherwise the server will reply with a “400 Bad Request”).

Now the CORS state machine is working fine, all the HTTP request is treated properly except the PUT request (the one sending the bitstream), the process stuck in the last call to br_sslio_read(). This is the very request that made me replace the Harmony NetPres (which apply wolfSSL) with bearSSL for more control over the package exchange because of the Maximum Fragment Length issue, but apparently bearSSL has taken care of that so the problem must have come from elsewhere. What is it? Well, time to dive into bearSSL source code again.

In SSL transmission, the bitstream send by the client is broken down into 16kbytes encrypted records, the full 16kbytes record has to be received in the bearssl input buffer (which we have assigned to the bearssl with br_ssl_engine_set_buffer()) in order to decrypt it. After a full record is received, bearssl engine will decrypt the record and store the plaintext data back into the input buffer. The encrypted and plaintext data share the same buffer. A call to br_sslio_read() read the plaintext data. After all plaintext data is read, another call to br_sslio_read() will reset the input buffer and get the engine to wait for another encrypted record to come in.

In the processing of a PUT request, for the purpose of allowing bitstream of any length to be received, we keep on calling br_sslio_read() until there is no more bitstream to be read. The problem happens at the end of the bitstream, we called br_sslio_read again which get the bearssl engine to run the low_read() function to read from TCP_RX_Buffer yet the buffer is empty,  low_read() is a block reading which means it will not return until at least 1 byte is read. Hence, the process stuck inside the waiting loop.

If I modify the low_read() to return -1 after a certain timeout, the bearssl engine will get into BR_ERR_IO state then the following br_sslio_write() will return -1 directly without writing the content which means I can not reply the client about the error at all. Returning a 0 in low_read() will only get the engine to call low_read() again right after.

In short, if I ever tried to read when the bitstream is all done, then I either get stuck in low_read() or the engine fail and won’t be able to write afterward.

Thus, if the bitstream is legit, I will stop the br_sslio_read() loop, write the remaining data and then reply a success read to the client. But if the bitstream is somewhat faulty then the process gets stuck in low_read(), hence the client is required to terminate the connection after a certain timeout, low_read() will be checking for that with TCPIP_TCP_WasReset() and exit the loop after the connection is terminated.

Next, I dump the remaining header content after the request line is read.
br_ssl_engine_recvapp_buf(ioc.engine, &alen);   //get the remaining length of plaintext data
br_ssl_engine_recvapp_ack(ioc.engine, alen);      //dump it
Now only the bitstream is stored inside the Flash which makes it easier to do the checksum

Then, I was assigned to replace the encryption or hash method used in hid.c which also rely on wolfSSL with bearSSL. This application is to generate the encrypted JSON packet which contains the configuration data and public key of the board for the registration with the server. To be continued.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.