Topic: STM32F4 HAL, FreeRTOS, LWIP and WolfSSL
I'm trying to build a simple client application using the above processor and packages. I have the ethernet driver functioning with the LWIP stack, and can make connections and exchange data with straight TCP connections. I have WolfSSL building, and almost running, with what I think is two last issues which I need help figuring out. When I try to connect via a TLS 1.2 connection to my host, I have the following problems.
1. Unable to verify host cert.
I believe this is because I don't have the proper PEM file (actually hardcoded since the demo doesn't have a filesystem) loaded. I was able to work around this by using wolfSSL_CTX_set_verify to set a callback that allows overriding this failure. I found this in the MQTT demo app. I don't think this is an issue, since it worked for the MQTT demo and allowed data exchange.
2. Failure in the Encrypt() function
The connection proceeds through wolfSSL_write into SendData, wolfSSL_negotiate, wolfSSL_connect, SendFinished, BuildMessage and into Encrypt. Once there, one of the first things Encrypt does is check if the encrypt ciphers are set up.
switch (ssl->encrypt.state) {
case CIPHER_STATE_BEGIN:
{
if (ssl->encrypt.setup == 0) {
WOLFSSL_MSG("Encrypt ciphers not setup");
return ENCRYPT_ERROR;
}
For some reason, they are not set up on my system, and I cannot figure out why, or where they were supposed to be set up.
The debug output, from the point where I override the cert failure up to this point is below.
Verified Peer's cert
wolfSSL Entering ERR_error_string
TLS Verify Callback: PreVerify 0, Error -188 (ASN no signer error to confirm failure)
Subject's domain name is wrs2test.hmps-apps.net
Allowing cert anyways
Verify callback overriding error!
wolfSSL Leaving DoCertificate, return 0
wolfSSL Leaving DoHandShakeMsgType(), return 0
wolfSSL Leaving DoHandShakeMsg(), return 0
received record layer msg
wolfSSL Entering DoHandShakeMsg()
wolfSSL Entering DoHandShakeMsgType
No KeyExchange required
processing server hello done
wolfSSL Leaving DoHandShakeMsgType(), return 0
wolfSSL Leaving DoHandShakeMsg(), return 0
connect state: HELLO_AGAIN
connect state: HELLO_AGAIN_REPLY
connect state: FIRST_REPLY_DONE
connect state: FIRST_REPLY_FIRST
wolfSSL Entering SendClientKeyExchange
wolfSSL Entering RsaEnc
wolfSSL Leaving RsaEnc, return 0
growing output buffer
Shrinking output buffer
wolfSSL Leaving SendClientKeyExchange, return 0
sent: client key exchange
connect state: FIRST_REPLY_SECOND
connect state: FIRST_REPLY_THIRD
growing output buffer
Shrinking output buffer
sent: change cipher spec
connect state: FIRST_REPLY_FOURTH
growing output buffer
wolfSSL Entering BuildMessage
Encrypt ciphers not setup
I'm using the user_settings.h file attached. I'm sure I must be missing something here, or not setting something up properly, but have been beating my head against the wall for two days trying to figure it out.
Minus my cert and not public server info, the following is the code I'm running. Hopefully someone can see where I've messed up from this.
int ssl_init_done = 0;
/* Define a structure to hold the WolfSSL context. */
WOLFSSL_CTX* xWolfSSL_Context;
WOLFSSL* xWolfSSL_Object = NULL;
static int tls_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
{
char buffer[80];
printf("TLS Verify Callback: PreVerify %d, Error %d (%s)\r\n", preverify,
store->error, wolfSSL_ERR_error_string(store->error, buffer));
printf(" Subject's domain name is %s\r\n", store->domain);
/* Allowing to continue */
/* Should check certificate and return 0 if not okay */
printf(" Allowing cert anyways\r\n");
return 1;
}
void test_secure(void)
{
struct hostent *server;
int socket_fd;
struct sockaddr_in ra;
int recv_data;
char data_buffer[1024];
int Step = 0;
while (Step >= 0)
{
switch (Step)
{
case 0 :
printf("Initializing WolfSSL if needed\r\n");
if (ssl_init_done == 0)
{
wolfSSL_Init();
ssl_init_done = 1;
}
break;
case 1 :
printf("Creating WolfSSL context if needed\r\n");
/* Attempt to create a context that uses the TLS V1.2 client protocol. */
if (ssl_init_done == 1)
{
xWolfSSL_Context = wolfSSL_CTX_new( wolfTLSv1_2_client_method() );
if( xWolfSSL_Context == NULL )
{
printf("Failed to create WolfSSL context\r\n");
Step = -1;
}
else
{
ssl_init_done = 2;
wolfSSL_CTX_set_verify(xWolfSSL_Context, SSL_VERIFY_PEER, tls_verify_cb);
}
}
break;
case 2 :
printf("Loading WolfSSL CA Certificate\r\n");
if (ssl_init_done == 2)
{
int rc = SSL_FAILURE;
/* Load CA certificate file */
rc = wolfSSL_CTX_load_verify_buffer(xWolfSSL_Context, cert, strlen(cert), SSL_FILETYPE_PEM);
if (rc != SSL_SUCCESS)
{
printf("Failed to load CA Certificate\r\n");
Step = -1;
}
else
ssl_init_done = 3;
}
break;
case 3 :
printf("Create the socket\r\n");
socket_fd = socket(PF_INET, SOCK_STREAM, 0);
if ( socket_fd < 0 )
{
printf("Socket call failed\r\n");
Step = -1;
}
break;
case 4 :
{
/* gethostbyname: get the server's DNS entry */
printf("Gethostbyname: get the %s server's DNS entry\r\n", HMPS_TEST_SECURE_HOST);
server = gethostbyname(HMPS_TEST_SECURE_HOST);
if (server == NULL)
{
printf("ERROR, no such host as %s\r\n", HMPS_TEST_SECURE_HOST);
Step = -1;
}
else
printf("Host %s resolved to IP address %s\r\n",server->h_name,inet_ntoa(*server->h_addr_list[0]));
}
break;
case 5 :
printf("Connecting to server.\r\n");
memset(&ra, 0, sizeof(struct sockaddr_in));
ra.sin_family = AF_INET;
memcpy((char *)&ra.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
ra.sin_port = htons(HMPS_TEST_SECURE_PORT);
if(connect(socket_fd,(struct sockaddr const*)&ra,sizeof(struct sockaddr_in)) < 0)
{
printf("Connect failed \n");
Step = -1;
}
else
printf("Connected to IP address %s\r\n",inet_ntoa(ra.sin_addr.s_addr));
break;
case 6 :
printf("Creating WolfSSL Object\r\n");
// The connect was successful. Create a WolfSSL object to associate with
// this connection. The context created during initialisation is passed as
// the function parameter.
if( xWolfSSL_Object == NULL )
xWolfSSL_Object = wolfSSL_new( xWolfSSL_Context );
if( xWolfSSL_Object == NULL )
{
printf("Failed to create WolfSSL Object\n");
Step = -1;
}
break;
case 7:
printf("Associating WolfSSL Object to socket\r\n");
// Associate the created WolfSSL object with the connected socket.
wolfSSL_set_fd( xWolfSSL_Object, socket_fd );
break;
case 8 :
{
char buf[1024];
int n;
sprintf(buf,HMPS_TEST_SECURE_MSG);
/* send the message line to the server */
printf("Send the message %s to the server\r\n", HMPS_TEST_SECURE_MSG);
wolfSSL_Debugging_ON();
n = wolfSSL_write( xWolfSSL_Object, buf, strlen(buf));
wolfSSL_Debugging_OFF();
if (n != strlen(buf))
{
printf("ERROR writing to socket\r\n");
Step = -1;
}
}
break;
case 9 :
printf("Receive data\r\n");
recv_data = wolfSSL_read( xWolfSSL_Object, data_buffer,sizeof(data_buffer));
if(recv_data < 0)
{
printf("Receive failed\r\n");
Step = -1;
}
else
{
data_buffer[recv_data] = 0x00; // Force terminating NULL
printf("Received data: %s\r\n", data_buffer);
}
break;
case 10 :
Step = -1;
break;
}
if (Step >= 0)
Step ++;
}
if( xWolfSSL_Object != NULL )
{
printf("Clean up WolfSSL\r\n");
// WolfSSL objects should be deleted when they are no longer required.
wolfSSL_free( xWolfSSL_Object );
xWolfSSL_Object = NULL;
// The WolfSSL context should be deleted if it is no longer required. However,
// because most deeply embedded applications will keep the context for the lifetime
// of the application, and only ever be restarted when the system is rebooted, it
// might be that the context is never explicitly freed.
// wolfSSL_CTX_free( xWolfSSL_Context );
// The library itself should be shut down cleanly if it too is no longer
// required. Again, because most deeply embedded applications will require the
// library for the lifetime of the application, and only ever be restarted when
// the system is rebooted, it might be that the library is never explicitly closed.
// wolfSSL_Cleanup();
}
close(socket_fd);
printf("Test complete\r\n");
}
Thanks in advance for any insight,
Jeff.