Assignment 5 (Programming Project): Multi-Level Secure Computer

Start date 5 October, design due 14 October, final deadline 28 October.

There has been discussion in class about the problem of building a really secure system: All our abstractions assume some underlying implementation that enforces the assumptions made in the abstractions. In this project you will answer this question, by designing and building (well, simulating) a multi-level secure computer.

The key idea is based on execution tracing: Capturing the security level of objects/subjects as the computer runs, and ensuring that security properties are not violated. Effectively what you will be building is a machine that enforces the simple security condition and *-property of the Bell-LaPadula model. You should base your design on Fenton's Data Mark Machine (Book section 16.4.1).

Objects (and subjects?) in the system will have a security level, which will affect how execution proceeds. You will prove that a program run on your machine will:

Minimum
Preserve the simple security condition and *-property for direct transfer of information (e.g., you can't directly read from a high value and write to a low value.)
Full Credit
Preserve the simple security condition and *-property for any flow of information (in the sense of Definition 16-1). This covers indirect information flow (e.g., conditions on high data affecting the result value of low data), as well as covert channel problems.

Security Model

You will operate on a simple level-based security model. There are four security levels (0-3), with 3 being the highest. In other words, the dom function is ≥.

The registers are tagged with a security level. You may view the input instruction as asking for a value at or below the security level of the register (e.g., people can write up into the system.) The output of the system is the registers, you can assume that at the end of execution, a register may be seen by anyone with at least sufficient clearance for the (fixed) level of that register. You must make sure high data doesn't get into low registers.

Simulator Overview

You should simulate a computer with 65536 8-bit bytes of memory, and 8 eight-bit registers labeled R0 (000), R1 (001), ..., R7 (111). Registers 0 and 1 are at level 0, 2 and 3 at level 1, 4 and 5 at level 2, and 6 and 7 at level 3. You'll have to think about what to do with memory - must it have a pre-defined level, or not? I suggest you assume a separate call/return (program counter) stack, not in the 64k of user-addressable memory.

Input will be read from standard input, output goes to standard output. The only output from the program is to print out all of the registers. You will initialize the system from a file program.txt that consists of assembly code, expressed as a decimal-coded address, a space, and an instruction (three decimal values). The rest of the line is a comment. An example program is available here, along with sample input and output. Please stick to the exact format of the output, to make automated testing scripts work (e.g., you should be able to cmp your_output output.txt and have it show no differences.) One caveat: You may find that you can't design a fully secure system that is capable of executing the sample program as written. If this is the case, you can either document the security leaks, or document why you aren't able to write something that executes this code as written. This will not be one of the test programs, but is intended to demonstrate some of the issues you must deal with. Execution starts at memory location 0: The program is stored in the 64k of user-addressable memory.

Instruction Set

Instructions are composed of a four bit op code, a four bit register code, and a two byte argument. The argument may be an address, a register, or nothing (just unused bytes) If a register, the low order bits (second byte) contain the register.

input register (op code 0)
Gets an input value from the input stream and places it in the given register. For your simulator, this means to read a line from standard input of the form x, where x is an integer from 0 to 255. The value is placed in register. Example:
27
39
127
You may choose to print a reminder (e.g., input value at level 2), but if you do so it must be on stderr, so it does not appear in the output when the output is piped to a file.
clear register (op code 1)
Puts 0 into register.
neg register1 (op code 2)
Negates register1 under two's complement arithmetic (inverts all bits and add 1). The effect should be register1 := 0 - register1.
load register, address (op code 3)
Loads the value at the given address into the given register.
store register, address (op code 4)
Stores the value in the given register at the given address.
add register1, register2 (op code 5)
Computes register1 + register2 mod 256. The result is placed in register1. You'll have to think about what this means if the two registers are not at the same security level.
skip register1 address (op code 6)
If the value in register1 is positive (high order bit not set), call address: Push the program counter onto the stack, then continue execution at address. (You should not save/restore the values of the registers - if you don't know what this means, then don't worry about it - some people trained in particular compiler/processor approaches might run into confusion here.) Again, you'll have to think about what this means based on the security level of the register - compare with the conditional and branch instructions of Fenton's machine.
return (op code 7)
Pop the program counter off the stack, thus resuming execution after the call. Return with an empty stack ends the program, at which point the values of all of the registers are printed to stdout (example).

Other Notes

To prove your system is completely secure, you may need to make some assumptions about the underlying system (for example, that the program halts.) Please list such restrictions. You may assume static checking validates that the restrictions are satisfied (even if such static checking is in fact undecidable). You will not be penalized if the test program finds security holes only because it fails to follow your assumptions, however unreasonable or unnecessary assumptions will be penalized.

An example of an unreasonable assumption would be that all operations between data at different levels is forbidden (e.g., skipped). An unnecessary assumption is one that your system handles correctly, i.e., you place restrictions on a program, but without those restrictions your computer would still be provably secure.

What to do

A one page design document is due October 14 (yes, shortly before the midterm) - it will not be graded, but will be used to give you feedback. The main form of the feedback will be a common page with hints/suggestions, although some of you may get individualized responses. Code is due October 28 (begining of class). While language is not specified, you must provide a makefile or a README file that gives simple instructions on how to build your system on mentor.ics or expert.ics. That will be the execution environment. You should also update the design description to include your assumptions and a discussion of why your system is secure (again, keep it short - try to stay within two typeset pages.)

You'll find this takes very little code, but reasoning about the security of your system will take as much time as you'll let it.

Turning in homework

Electronic submission required, using the turnin command (on mentor.ics.purdue.edu or expert.ics.purdue.edu, turnin -c cs526 -p asn5 filename). Pdf is the safest for capturing non-text, please check with the TA for formats other than text or pdf.


Valid XHTML 1.1!