Couple NSC calls to Task

Hi !

I have practiced with LPC55S69 and FreeRTOS with secure context a bit. RTOS running on nonsecure world and execute NSC calls to Secure side.
Each task is need to allocate secure context for be able to execute NSC calls to secure side.
But it is not any limitation what NSC call can call one task but not other one.
Have it sense to extend Secure context with API for identify from what task NSC call is executed.
In this case secure side can check calling context and reject calls from “unknown” tasks.
Or how to limit access to some secure API for some tasks ?

Regards, Eugene

You have the flexibility to provide code for both the secure and non secure sides, so can implement a scheme that checks where the call to secure function originated (in a similar way to to how the the kernel code checks that any attempt to escalate privilege came from a kernel API function, and rejects any other), or by having tasks present an identity given to it by trusted code.

I try to understand if this feature is planned in your roadmap at some point and have it sense at all.
As you suggest , 2 variants are doable:

  1. At task creation point ( before scheduler starts ) , provide task handler to secure side.
    When task starts and secure context is created , add handler info to it as well.
    When API is called , retrieve task id from current context and compare with known one.

  2. Reject call already at Nonsecure side, even before calling of secure side.
    Task handler is known in SVC and each task can have hardcoded list of enabled NSC APIs.

What way is looks more “secure” ?

I think it is not a problem to add Taskhandler to SecureContextHandle_t structure.
But some safe API is need on Secure side for save it somewhere for be able to compare it
with known value.
Or this kind of instrumentation is not so safe ? It propagate task handler on Secure side
with all side effects. But Secure side is trusted side.

Are you wanting to protect against a system that has been compromised, so you want to prevent malicious code making a secure call. Or are you wanting to protect against a scenario where you have statically linked in a library that is un-trusted because it comes from a third party - and you want to ensure nothing in that third party library can make a secure call - or something else?

I think it is more like restrictions where known components use known NSC API only.
System consist of several tasks with usage of 3rd party libraries as well.
Tasks running in unprivileged mode and data protected by MPU. But NSC gate is completely open and better to restrict that not.
Or you think in current “little OS” concept it dosn’t have sense at all ?

I don’t think there is a generic answer to that as each use case is different and so has a different threat model. Many statically linked small systems that do not dynamically load tasks can use techniques such as code signing to ensure an executable image has not been tampered with, the MPU to ensure code cannot execute from RAM to prevent code injection, task limit registers to catch stack overflows, MPU to separate third party code from first party code, etc. - but these examples assume a relatively small simple system.

Even so - security in layers is always something we aim for, so this is a good conversation to have. We have our own ideas on how to protect the secure gateway calls from unprivileged tasks, but I would like to hear other peoples’ ideas too. What do you think would be good mechanisms for this? For maximum protection tasks would have to have un-spoofable identities that could be queried from the secure side.

For prototype purposes I have added task handler to Secure context ( better to use any other id).
As result is possible to have API what always return caller task id on secure side.
And caller task id should be known and provisioned at task creation point with one time provisioned protection.

if (!(TaskHandleTaskGet() && (TaskHandleTaskGet() == TaskHandleGetCurrent())))
{
return -1;
}

And provisioned part as NSC call.

/* Can be set one time in Privileged mode. */
if (inPrivilegedMode() && handler && !TaskTaskHandler)
{
TaskTaskHandler = handler;
}

But may be better to get current task id by using call to nonsecure part always.

By the way I can see list of restrictions for each task ( APIs, SVC calls and etc.) are gain popular recently. Just day ago this material was published :

If my understanding of the attached is correct then the RTOS is treated as trusted code and runs privileged, and the advice is to run all application code unprivileged - which is of course sound advice. You can then have an immutable bootloader check the signature of the application executable, as per our OTA examples, to ensure you are booting a genuine and un-tampered copy of the firmware, and as per our previous conversation above, use the MPU to ensure memory access is restricted and that a bad actor cannot execute code from RAM, plus use secure elements/enclaves to hide any secret information such as encryption keys from the application code or an attacker. All that gives the groundwork for the security of the system - but security in layers is always good so we then look further and consider that unexpected code (which could be malicious, or in a third party library, or simply a bug in the application code) is executing and we want to prevent that from making a call to the secure side - or indeed go further and restrict access to certain RTOS API functions.

Putting in place binary restrictions as in if code is executing privileged then it can make restricted calls, and if unprivileged it can’t, is the simplest thing as you can query the hardware to determine that.

To go further, and as per our discussions here, and your post above, tasks need to be assigned an un-spoofable and query-able identity. If we trust the boot sequence where identities are assigned, and the memory protection to prevent access to kernel memory, then that identity can be stored in the task control block and queried from the secure side - or the secure side could be given a list of authorised tasks as part of the boot sequence. Alternatively statically allocated tasks that are authorised to make secure calls can be placed in a memory segment specifically marked as having the rights to access the secure side, extending slightly the scheme we used today to place priviliged code in specially marked memory regions (by using linker variables).

Did I capture everything here?

Hi Richard !
Thank you !
I can see rules of big systems is start to be applicable for small one and guys try to sell those for extend securiy. Others OS is follow as well ( zephyr ?) at some level.
At list usage if identity id for tasks and other RTOS object have sense.
Your proposal for coupling NSC call looks good and I think have sense for our system as well.
And you agree with fact that it should increase security level a bit.
Is this so ?

Now I will try to undestand how-to attach secure MPU changes to each NSC call for masking
all other secure code from global visibility. Even it fully trusted, it should be partitioned as well.

Regards,
Eugene