SharkSSL™ Embedded SSL/TLS Stack

Detailed Description

The SharkSSL API is optimized for both bare metal systems and RTOS environments. Directly using the SharkSSL API is recommended if you plan on integrating SharkSSL with an existing application or if you do not plan on using any of the extra libraries such as the WebSocket libraries.

You need the socket example library (selib.c) if you plan on running any of our examples or if you plan on using any of the higher level protocols such as the WebSocket libraries.

The socket example library selib is based on sequential code (non event driven) and uses blocking socket calls. The sequential example code can still be used in event driven systems by using SeCtx.

SeCtx is a Context Manager designed to make the sequential code cooperate with event driven systems. Sequential code is much easier to design than event driven code. The Context Manager also removes the complexity in using TCP/IP stacks such as uIP since it lets a more traditional socket API function on a non standard event driven TCP/IP stack.

Figure 1 shows how SeCtx enables sequential code to cooperate with event driven code

Non RTOS systems are typically event driven, and the code using these systems cannot typically utilize blocking function calls such as waiting for socket data. The SeCtx library makes it possible to use selib and all higher level protocols in any event driven system.

The lwIP Raw/TCP socket porting library and the uIP porting library utilize SeCtx.

How to use SeCtx

The exact initialization is platform dependent. See the readme file for uIP or lwIP-raw for details.In general, the SeCtx makes regular and sequential code cooperate with an event driven system.

You start by declaring a thread/task

void mainTask(SeCtx* ctx)
{
static SOCKET sock; /* Must be static */
SOCKET_constructor(&sock, ctx); /* Set SeCtx in the socket struct */
se_connect(&sock, "myserver", 8888); /* Cooperatively yield */
for(;;) /* Main loop */
{
char buf[100];
se_recv(&sock, buf, sizeof(buf), INFINITE_TMO); /* Cooperatively yield */
/* use received data */
}
}

Add the following SeCtx code to your system's main loop.

void myMainLoop(void)
{
/* buffer must be sufficiently large for preserving/storing the stack */
static char stackbuf[512];
static SeCtx ctx; /* Context Manager (magic stack handler) */
SeCtx_constructor(&ctx, mainTask, stackbuf, sizeof(stackbuf));
for(;;)
{
if(busy) /* your own logic (event scheduler busy) */
{
// manage your regular event driven code
}
else /* Else: system is idle */
{
SeCtx_run(&ctx); /* run the context manager */
}
}
}

Notice how we pass in the function pointer 'mainTask' when we call the SeCtx constructor. The function pointer is used the first time SeCtx_run is called.

The mainTask function above should be designed as if it runs on an RTOS. You can use normal sequential code and you do not need to create a state machine, which is required for standard event driven code.

When you call one of the socket functions, such as se_connect or se_recv, the context manager preserves the current stack and yields. The code returns out of SeCtx_run and back into your regular event driven code. The task is resumed when socket data is available.

The variable 'stackbuf' is used by SeCtx when yielding the thread. The stack is copied to this buffer. Function SeCtx_panic will be called if the buffer is too small for storing the stack. SeCtx_panic is a function you must include in your code:

void SeCtx_panic(SeCtx* o, U32 size)
{
printf("PANIC: stack size required: %u",size);
for(;;); /* stop system or reboot */
}

Limitations:

Function SeCtx_run must always be called at the same stack depth and should be called from your main event loop.

You may declare data on the stack and this data will be preserved during a context switch; however, the SOCKET object cannot be on the stack. The SOCKET object must be declared static, be a global variable, or be allocated from the heap.

Dynamic tasks/threads

Each SeCtx instance is associated with one task – the function passed into the SeCtx constructor. The task function is called the first time SeCtx_run is called – in other words, the task is created the first time SeCtx_run is called. This function should connect a socket and stay in a forever loop. The function may return if the socket is closed/terminated. When the task function returns, SeCtx_run returns a negative number. You may use the return value to detect when a task terminates. You must make sure to terminate the SeCtx object and make sure to not call SeCtx_run again with this object if you do not want the task to be re-created. The task is automatically re-created if SeCtx_run is called again with the same SeCtx object. Re-creating a task may be a feature you want as part of your design as a way to re-connect a socket that closed unexpectedly.

Multiple tasks/threads

You may create multiple SeCtx instances and associate each instance with a different task function. You must call SeCtx_run for each SeCtx instance. As an example, assume we have three task instances, ctx1 to ctx3. The following modification to the "main loop" example above makes sure we execute each task in order.

else /* Else: system is idle */
{
SeCtx_run(&ctx1); /* Execute task 1 */
SeCtx_run(&ctx2); /* Execute task 2 */
SeCtx_run(&ctx3); /* Execute task 3 */
}
}

Modules

 Context Manager
 The SeCtx library makes it possible to use sequential functions in an event driven system such as a bare metal (non RTOS) system.