userspace: sample app domain applications

Added a sample using the app_memory submodule to protect memory
used by three threads.

Signed-off-by: Shawn Mosley <smmosle@tycho.nsa.gov>
This commit is contained in:
Shawn Mosley 2018-06-29 09:30:24 -04:00 committed by Anas Nashif
commit 26c51c43d0
8 changed files with 625 additions and 0 deletions

View file

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.9.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
#add_definitions( -DALTMSG)
#add_definitions( -DDBUG)

View file

@ -0,0 +1,58 @@
.. _userspace_protected_memory:
Userspace Protected Memory
##########################
Overview
********
This sample is an example of running multiple threads assigned
unique memory domains with protected partitions. The
application uses memory partitioning with a sample algorithm
simulating an enigma-like machine, but the implementation of the
machine has not been validated and should not be used for any
actual security purposes.
Requirments
***********
The sample is dependent on the subsystem app_memory, and it will
not run on boards that do not support the subsystem. The sample
was tested on the following boards qemu_x86,frdm_k64, an 96b_carbon.
Building and Running
********************
This example will only cover the qemu_x86 board, since the sample
just prints text to a console.
.. zephyr-app-commands::
:zephyr-app: samples/basic/userspace/shared_mem
:board: qemu_x86
:goals: build run
:compact:
After starting, the console will display multiple starting messages
followed by two series of repeating messages. The repeating messages
are the input and output of the enigma-like machine. The second
message is the output of the first mesessage, and the resulting
output is the first message without spaces. The two messages are
marked as 1 and 1' respectively.
Two definitions can be inserted to change the wheel settings and print
the state information. To enable the definitions uncomment the last
two lines in CMakelists.txt.
Functionality
*************
The PT thread sends a message followed by the encrypted version of the
message after sleeping. To send the message the PT thread checks a
flag, and if it is clear, writes the message to a buffer shared with
the encrypt thread. After writing the buffer, the flag is set. The
encrypt thread copies the memory from the common buffer into the
encrypted thead's private memory when the flag is set and then clears
the flag. Once the encrypted thread receives the text string, it
performs a simulation of the enigma machine to produce cypher text(CT).
The CT is copied to a shared memory partition connecting to the third
thread. The third thread prints the CT to the console with a banner
denoting the content as CYPHER TEXT.

View file

@ -0,0 +1,3 @@
CONFIG_USERSPACE=y
CONFIG_APPLICATION_MEMORY=n
CONFIG_APP_SHARED_MEM=y

View file

@ -0,0 +1,17 @@
sample:
description: userspace memory domain protection
example application
name: protected memory
platforms: all
common:
tags: samples
harness: console
harness_config:
type: one_line
regex:
- "MSG"
tests:
kernel.memory_protection.userspace:
filter: CONFIG_ARCH_HAS_USERSPACE
tags: userspace samples

View file

@ -0,0 +1,161 @@
/*
* enc.c
*/
/*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This is a model of the enigma hardware as remembered from
* a description read around ten years prior. The model has
* not been tested or validated as cryptographically secure.
* Furthermore the author intentionally did not validate the
* model against published descriptions of the enigma. The
* only test completed was to set the wheels to a known
* position and insert a pt string of text into the function.
* The output was recorded as ct. The wheels were reset to the
* original start position, and the recorded ct was inserted
* into the function. The output was compared to the original
* pt text and matched. The result of the test matched the
* expected outcome, but it does not validate the algorithm
* meets any security requirments.
*
* **********************************************************
*
* DO NOT USE IN PRODUCTION CODE AS A SECURITY FEATURE
*
* **********************************************************
*/
#include "enc.h"
/*
* for a pt character encrypt a ct character and update wheels
* Each wheel must be indexed into and out of the wheel arrays
* this process is based on a absolute index of input characters
* and the reflector.
* Note: the output of wheel arrays are added to WHEEL_SIZE
* to prevent a negative index when subtracting the iw value.
* The printk lines have been left in to inspect operations
* of the enigma simulation. simply add the definition DBUG
* to enable the detailed messages.
*/
char enig_enc(char pt)
{
short tmpIndex;
char ct;
#ifdef DBUG
printk("\nEE PT: %c, %02x\n", pt, pt);
printk("Index: %d, %d, %d\n", IW1, IW2, IW3);
#endif
tmpIndex = char_to_index(pt);
#ifdef DBUG
printk("EE : %02x\n", tmpIndex);
#endif
/* if error return */
if (tmpIndex == -1)
return (char)0xFF;
tmpIndex = (W1[IMOD(IW1, tmpIndex)] + WHEEL_SIZE - IW1) % WHEEL_SIZE;
#ifdef DBUG
printk("EE i1: %02x\n", tmpIndex);
#endif
tmpIndex = (W2[IMOD(IW2, tmpIndex)] + WHEEL_SIZE - IW2) % WHEEL_SIZE;
#ifdef DBUG
printk("EE i2: %02x\n", tmpIndex);
#endif
tmpIndex = (W3[IMOD(IW3, tmpIndex)] + WHEEL_SIZE - IW3) % WHEEL_SIZE;
#ifdef DBUG
printk("EE i3: %02x\n", tmpIndex);
#endif
tmpIndex = R[tmpIndex];
#ifdef DBUG
printk("EE r: %02x\n", tmpIndex);
#endif
tmpIndex = (W3R[IMOD(IW3, tmpIndex)] + WHEEL_SIZE - IW3) % WHEEL_SIZE;
#ifdef DBUG
printk("EE i3: %02x\n", tmpIndex);
#endif
tmpIndex = (W2R[IMOD(IW2, tmpIndex)] + WHEEL_SIZE - IW2) % WHEEL_SIZE;
#ifdef DBUG
printk("EE i2: %02x\n", tmpIndex);
#endif
tmpIndex = (W1R[IMOD(IW1, tmpIndex)] + WHEEL_SIZE - IW1) % WHEEL_SIZE;
#ifdef DBUG
printk("EE i1: %02x\n", tmpIndex);
#endif
ct = index_to_char(tmpIndex);
#ifdef DBUG
printk("EE CT: %02x\n", ct);
#endif
/* test ct value or just return error ? */
update_wheel_index();
return ct;
}
/*
* calc reverse path for wheel
* this simplifies the reverse path calculation
* Return: 1:ok -1 error
*/
int calc_rev_wheel(BYTE *wheel, BYTE *backpath)
{
int i;
for (i = 0; i < WHEEL_SIZE; i++) {
if (wheel[i] >= WHEEL_SIZE || wheel[i] < 0) {
return -1;
}
backpath[wheel[i]] = i;
}
return 1;
}
/*
* convert a-z to 0-25
*/
short char_to_index(char c)
{
if (c < 'a' || c > 'z') {
return -1;
}
return (short)(c - 'a');
}
/*
* convert from a index 0-25 to a-z
*/
char index_to_char(short i)
{
if (i < 0 || i > 25) {
return 0xFF;
}
return (char)((short)'a' + i);
}
/*
* basic update to wheels based on full rotation
* of prior wheel. This could be modified to change
* the direction of rotation or order of updates
*/
void update_wheel_index(void)
{
IW1++;
if (IW1 >= WHEEL_SIZE) {
IW1 %= WHEEL_SIZE;
IW2++;
}
if (IW2 >= WHEEL_SIZE) {
IW2 %= WHEEL_SIZE;
IW3++;
}
if (IW3 >= WHEEL_SIZE) {
IW3 %= WHEEL_SIZE;
}
}

View file

@ -0,0 +1,37 @@
/* enc.h */
/*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ENC_H
#define ENC_H
#include <misc/printk.h>
#define WHEEL_SIZE 26
#define IMOD(a, b) ((a + b) % WHEEL_SIZE)
#ifndef BYTE
#define BYTE unsigned char
#endif
void update_wheel_index(void);
char index_to_char(short i);
short char_to_index(char c);
int calc_rev_wheel(BYTE *wheel, BYTE *backpath);
char enig_enc(char pt);
extern BYTE W1[26];
extern BYTE W2[26];
extern BYTE W3[26];
extern BYTE R[26];
extern BYTE W1R[26];
extern BYTE W2R[26];
extern BYTE W3R[26];
extern int IW1;
extern int IW2;
extern int IW3;
#endif

View file

@ -0,0 +1,280 @@
/* main.c */
/*
* SPDX-License-Identifier: Apache-2.0
*/
/*
*
* Basic example of userspace thread protected memory
*
* NOTE: The encryption algorithim is unverified and
* based on a 1930's erra piece of hardware.
* DO NOT USE THIS CODE FOR SECURITY
*
*/
#include "main.h"
#include "enc.h"
/* the following definition name prefix is to avoid a conflict */
#define SAMP_BLOCKSIZE 50
/*
* The memory partitions have been named to simplify
* the definition of variables. A possible alternative
* is using one source file per thread and implementing
* a objcopy to rename the data and bss section for the
* thread to the partiotion name.
*/
/* prepare the memory partition structures */
FOR_EACH(appmem_partition, part0, part1, part2, part3, part4);
/* prepare the memory domain structures */
FOR_EACH(appmem_domain, dom0, dom1, dom2);
/* each variable starts with a name defined in main.h
* the names are symbolic for the memory partitions
* purpose.
*/
_app_red_b BYTE fBUFIN;
_app_red_b BYTE BUFIN[63];
_app_blk_b BYTE fBUFOUT;
_app_blk_b BYTE BUFOUT[63];
/* declare and set wheel and reflector */
/* To use add definition ALTMSG */
#ifdef ALTMSG
_app_enc_d BYTE W1[26] = START_WHEEL;
#else
_app_enc_d BYTE W1[26] = START_WHEEL2;
#endif
_app_enc_d BYTE W2[26] = START_WHEEL;
_app_enc_d BYTE W3[26] = START_WHEEL;
_app_enc_d BYTE R[26] = REFLECT;
_app_enc_b int IW1;
_app_enc_b int IW2;
_app_enc_b int IW3;
/*
* calculated by the enc thread at init and when the wheels
* change.
*/
_app_enc_b BYTE W1R[26];
_app_enc_b BYTE W2R[26];
_app_enc_b BYTE W3R[26];
/*
* sync threads
*/
K_SEM_DEFINE(allforone, 0, 3);
__kernel struct k_thread enc_thread;
K_THREAD_STACK_DEFINE(enc_stack, STACKSIZE);
__kernel struct k_thread pt_thread;
K_THREAD_STACK_DEFINE(pt_stack, STACKSIZE);
__kernel struct k_thread ct_thread;
K_THREAD_STACK_DEFINE(ct_stack, STACKSIZE);
_app_enc_d char encMSG[] = "ENC!\n";
_app_enc_d int enc_state = 1;
_app_enc_b char enc_pt[50]; /* Copy form shared pt */
_app_enc_b char enc_ct[50]; /* Copy to shared ct */
_app_user_d char ptMSG[] = "PT: message to encrypt\n";
/* encrypted message when W1 = START_WHEEL */
/* to use add definition ALTMSG */
#ifdef ALTMSG
_app_user_d char ptMSG2[] = "nfttbhfspfmdqzos\n";
#else
/* encrypted message when W1 = START_WHEEL2 */
_app_user_d char ptMSG2[] = "ofttbhfspgmeqzos\n";
#endif
_app_ct_d char ctMSG[] = "CT!\n";
void main(void)
{
k_tid_t tPT, tENC, tCT;
k_thread_access_grant(k_current_get(), &allforone, NULL);
/* initialize the partition structures */
FOR_EACH(appmem_init_part, part0, part1, part2, part3, part4);
printk("init partitions complete\n");
appmem_init_app_memory();
printk("init app memory complete\n");
/*
* create an enc thread init the memory domain and add partitions
* then add the thread to the domain.
*/
tENC = k_thread_create(&enc_thread, enc_stack, STACKSIZE,
(k_thread_entry_t)enc, NULL, NULL, NULL,
-1, K_USER,
K_FOREVER);
k_thread_access_grant(tENC, &allforone, NULL);
/* use K_FOREVER followed by k_thread_start*/
printk("ENC Thread Created %08X\n", (unsigned int) tENC);
appmem_init_domain_dom1(part2);
printk("init domain complete\n");
appmem_add_part_dom1(part1);
appmem_add_part_dom1(part3);
printk("Partitions added to dom1\n");
appmem_add_thread_dom1(tENC);
printk("dom1 Created\n");
tPT = k_thread_create(&pt_thread, pt_stack, STACKSIZE,
(k_thread_entry_t)pt, NULL, NULL, NULL,
-1, K_USER,
K_FOREVER);
k_thread_access_grant(tPT, &allforone, NULL);
printk("PT Thread Created %08X\n", (unsigned int) tPT);
appmem_init_domain_dom0(part0);
appmem_add_part_dom0(part1);
appmem_add_thread_dom0(tPT);
printk("dom0 Created\n");
tCT = k_thread_create(&ct_thread, ct_stack, STACKSIZE,
(k_thread_entry_t)ct, NULL, NULL, NULL,
-1, K_USER,
K_FOREVER);
k_thread_access_grant(tCT, &allforone, NULL);
printk("CT Thread Created %08X\n", (unsigned int) tCT);
appmem_init_domain_dom2(part4);
appmem_add_part_dom2(part3);
appmem_add_thread_dom2(tCT);
printk("dom2 Created\n");
k_thread_start(&enc_thread);
/* need to start all three threads. let enc go first to perform init step */
printk("ENC thread started\n");
k_thread_start(&pt_thread);
printk("PT thread started\n");
k_thread_start(&ct_thread);
k_sem_give(&allforone);
printk("CT thread started\n");
k_thread_abort(k_current_get());
}
/*
* The enc thread.
* Function: initialize the the simulation of the wheels.
* Copy memory from pt thread and encrypt to a local buffer
* then copy to the ct thread.
*/
void enc(void)
{
int index, index_out;
if (enc_state == 1) {
fBUFIN = 0; /* clear flags */
fBUFOUT = 0;
calc_rev_wheel((BYTE *) &W1, (BYTE *)&W1R);
calc_rev_wheel((BYTE *) &W2, (BYTE *)&W2R);
calc_rev_wheel((BYTE *) &W3, (BYTE *)&W3R);
IW1 = 0;
IW2 = 0;
IW3 = 0;
enc_state = 0;
}
while (1) {
k_sem_take(&allforone, K_FOREVER);
if (fBUFIN == 1) { /* 1 is process text */
printk("ENC Thread Received Data\n");
/* copy message form shared mem and clear flag */
memcpy(&enc_pt, BUFIN, SAMP_BLOCKSIZE);
printk("ENC PT MSG: %s\n", (char *)&enc_pt);
fBUFIN = 0;
/* reset wheel: probably better as a flag option */
IW1 = 7;
IW2 = 2;
IW3 = 3;
/* encode */
memset(&enc_ct, 0, SAMP_BLOCKSIZE); /* clear memory */
for (index = 0, index_out = 0; index < SAMP_BLOCKSIZE; index++) {
if (enc_pt[index] == '\0') {
enc_ct[index_out] = '\0';
break;
}
if (enc_pt[index] >= 'a' && enc_pt[index] <= 'z') {
enc_ct[index_out] = (BYTE)enig_enc((BYTE) enc_pt[index]);
index_out++;
}
}
/* test for CT flag */
while (fBUFOUT != 0) {
k_sleep(100);
}
/* ct thread has cleared the buffer */
memcpy(&BUFOUT, &enc_ct, SAMP_BLOCKSIZE);
fBUFOUT = 1;
}
k_sem_give(&allforone);
}
}
/*
* the pt function pushes data to the enc thread.
* It can be extended to receive data from a serial port
* and pass the data to enc
*/
void pt(void)
{
k_sleep(2000);
while (1) {
k_sem_take(&allforone, K_FOREVER);
if (fBUFIN == 0) { /* send message to encode */
printk("\nPT Sending Message 1\n");
memset(&BUFIN, 0, SAMP_BLOCKSIZE);
memcpy(&BUFIN, &ptMSG, sizeof(ptMSG));
/* strlen should not be used if user provided data, needs a max length set */
fBUFIN = 1;
}
k_sem_give(&allforone);
k_sem_take(&allforone, K_FOREVER);
if (fBUFIN == 0) { /* send message to decode */
printk("\nPT Sending Message 1'\n");
memset(&BUFIN, 0, SAMP_BLOCKSIZE);
memcpy(&BUFIN, &ptMSG2, sizeof(ptMSG2));
fBUFIN = 1;
}
k_sem_give(&allforone);
k_sleep(5000);
}
}
/*
* CT waits for fBUFOUT = 1 then copies
* the message clears the flag and prints
*/
void ct(void)
{
char tbuf[60];
while (1) {
k_sem_take(&allforone, K_FOREVER);
if (fBUFOUT == 1) {
printk("CT Thread Receivedd Message\n");
memset(&tbuf, 0, sizeof(tbuf));
memcpy(&tbuf, BUFOUT, SAMP_BLOCKSIZE);
fBUFOUT = 0;
printk("CT MSG: %s\n", (char *)&tbuf);
}
k_sem_give(&allforone);
}
}

View file

@ -0,0 +1,61 @@
/* main.h */
/*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef MAIN_H
#define MAIN_H
#include <zephyr.h>
#include <misc/printk.h>
#include <kernel_structs.h>
#include <string.h>
#include <stdlib.h>
#include <app_memory/app_memdomain.h>
#include <misc/util.h>
#if defined(CONFIG_ARC)
#include <arch/arc/v2/mpu/arc_core_mpu.h>
#endif
void enc(void);
void pt(void);
void ct(void);
#define _app_user_d _app_dmem(part0)
#define _app_user_b _app_bmem(part0)
#define _app_red_d _app_dmem(part1)
#define _app_red_b _app_bmem(part1)
#define _app_enc_d _app_dmem(part2)
#define _app_enc_b _app_bmem(part2)
#define _app_blk_d _app_dmem(part3)
#define _app_blk_b _app_bmem(part3)
#define _app_ct_d _app_dmem(part4)
#define _app_ct_b _app_bmem(part4)
/*
* Constant
*/
#define STACKSIZE 1024
#define PRIORITY 7
#define BYTE unsigned char
#define START_WHEEL {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, \
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}
#define START_WHEEL2 {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, \
17, 18, 19, 20, 21, 22, 23, 24, 25, 5, 0, 4, 1, 3, 2}
#define REFLECT {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, \
15, 14, 17, 16, 19, 18, 21, 20, 23, 22, 25, 24}
#endif