Skip to content

A Quick Introduction to Jeeves

Jean Yang edited this page Jun 9, 2015 · 4 revisions

The Jeeves language is embedded in Python, which means that we have developed Jeeves as a Python library. We've done this by creating special objects corresponding to faceted values and overloading Python operators to handle computations on these objects. Since Python doesn't allow overloading of everything, we have implemented an @jeeves macro that rewrites classes and functions to use the appropriate Jeeves library functions.

For further reference, check out the Jeeves API documentation.

Importing.

To access functions to create labels, sensitive values, and policies, you must import the Jeeves library.

import JeevesLib

Jeeves has its own functions to handle assignments, conditionals, and more. To help the programmer write code that looks like Python code but uses the Jeeves semantics, we have a the @jeeves macro.

from sourcetrans.macro_module import macros, jeeves

The Jeeves source transformation uses the MacroPy module, which requires the module macropy.activate to be imported before any other modules. A way to do ensure this happens at the beginning is to add import macropy.activate to the beginning of your __init__.py file and then import X for all modules X that use the @jeeves macro.

Labels, Facets, and Confidentiality Policies.

The Jeeves libraries manages different views of sensitive values and is responsible for producing appropriate values for the results. In Jeeves terms, a faceted value is a sensitive value containing two views, a high-confidentiality and a low-confidentiality view. Faceted values are guarded by labels, which are either high or low and determine which facet flows to the viewer. Programmer-specified policies determine the label values; these policies are functions that describe whether a viewer has access. Computations on faceted values produce new faceted values, so all computations interacting with outside world (such as print) need to concretize values. Concretization occurs with respect to a specific viewer and uses the policies to assign label values in order to project out a result adhering to the policies. The Jeeves library has functionality for creating labels, creating faceted values, associating labels with policies, and concretizing faceted values.

We call mkLabel to make a label and restrict to make a policy:

x = JeevesLib.mkLabel()
JeevesLib.restrict(x, lambda _: False)

The restrict function takes the label x and a function that takes an output channel and returns whether x may be high with respect to that output channel. The call to restrict above returns False no matter the output channel. No matter what output channel argument we supply when we we call the library's concretize function, we will get that x is low (False):

assertFalse(JeevesLib.concretize(x, None))

We can make sensitive values with these labels:

xS = JeevesLib.mkSensitive(x, 42, 0)

Here, xS is a sensitive value guarded by label x where 42 is the high-confidentiality facet and 0 is the low-confidentiality facet. Because, as we showed, x is always low, xS always concretizes to 0.

We can write a less trivial policy as follows:

x = JeevesLib.mkLabel()
JeevesLib.restrict(x, lambda y: y == 2)
value = JeevesLib.mkSensitive(x, 42, 41)

The policy says that if the output channel is equal to 2, then the label x may be high. Thus the concretization of value will only be 42 if the output channel argument is 2:

assertEquals(JeevesLib.concretize(1, value), 41)
assertEquals(JeevesLib.concretize(2, value), 42)
assertEquals(JeevesLib.concretize(3, value), 41)

Contexts that Depend on Sensitive Values

Contexts may also depend on sensitive values. Consider what happens in the following code:

x = JeevesLib.mkLabel()
JeevesLib.restrict(x, lambda ctxt : ctxt == 42)
value = JeevesLib.mkSensitive(x, 42, 20)
self.assertEquals(JeevesLib.concretize(value, value), 42)

In this code, we create a label and store it in x. We attach a policy to x that the context must be equal to 42. We then create a sensitive value that x is either equal to 42 or 20, depending on the value of x.

Something interesting happens when we use value as the context for viewing value. We create a circular dependency between value and the policy on label x. As a result, the policy allows multiple outcomes: either x is low and value is 20 or x is high and value is 42. In these cases, the Jeeves runtime allows the label to be high, and thus JeevesLib.concretize(value, value) yields 42.

Conditionals, Functions, and More

The @jeeves macro rewrites functions to use the Jeeves versions of conditionals, assignments, and function calls.

Larger Programs

You may look at our gallery to see how we use Jeeves for larger programs. The @jeeves macro also works for classes.