Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SIFR

SIFR tracks how sensitive data moves through a program at compile time. A value annotated as Secret cannot be passed to a logger, printed, or returned from a public API without an explicit label change; the compiler rejects the code. The same mechanism covers integrity: data from an untrusted source cannot silently flow into a context that requires trusted input.

The library models this with phantom type parameters on a wrapper type. No runtime cost is involved; the labels exist only in the type system.

Labels

Every value is wrapped in Labeled<T, C, I> where C is a confidentiality label and I is an integrity label. Both are zero-cost with no runtime overhead.

The crate ships two built-in label sets.

Confidentiality: Public ≤ Internal ≤ Secret. A Public value can be used where a Secret is expected; the reverse is a compile error.

Integrity: Trusted at the top, Untrusted at the bottom. A Trusted value flows freely to Untrusted. Going the other direction requires relabel!.

use sifr::{Labeled, Secret, Trusted};

let password: Labeled<String, Secret, Trusted> = Labeled::new("hunter2".into());

Relabeling

relabel! changes the label of a value. Outside a labeled_branch! block it is unconditional. Inside one, the compiler checks that the program-counter label is no higher than the target; a violation is a compile error.

use sifr::{relabel, Labeled, Public, Secret, Trusted};

// raise: always allowed
let s: Labeled<i32, Secret, Trusted> = relabel!(Labeled::<i32, Public, Trusted>::new(1), Secret, Trusted);

// lower: allowed here because there is no enclosing branch
let p: Labeled<i32, Public, Trusted> = relabel!(Labeled::<i32, Secret, Trusted>::new(1), Public, Trusted);

Implicit flows

Branching on a Labeled value is an implicit flow. labeled_branch! tracks the program-counter label for the branch body and rejects any relabel! that the condition label would forbid.

use sifr::{labeled_branch, Labeled, Secret, Trusted};

let cond: Labeled<bool, Secret, Trusted> = Labeled::new(true);

labeled_branch!(if cond {
    // pc = Secret.
    // relabel!(val, Public, Trusted) is a compile error here.
});

Supported constructs: if, if let, else if, while, while let, match. In an else if chain the pc is the join of all conditions evaluated so far.

Custom label lattices

#[derive(Label)] builds a label lattice from an enum. The derive macro computes the transitive closure and verifies that every pair of labels has a unique least upper bound.

use sifr::Label;

#[derive(Label)]
pub enum Clearance {
    Public,
    #[above(Public)]
    Confidential,
    #[above(Confidential)]
    TopSecret,
}

Diamond lattices:

#[derive(Label)]
pub enum Compartment {
    Unclassified,
    #[above(Unclassified)]
    Alpha,
    #[above(Unclassified)]
    Beta,
    #[above(Alpha, Beta)]
    Full,
}

Derived labels implement both ConfLabel and IntegLabel and can be used in either position.

Calling labeled functions

labeled_call! and labeled_get! call ordinary functions with Labeled arguments. The result label is the join of all argument labels.

use sifr::{labeled_call, labeled_get, Labeled, Public, Secret, Trusted};

fn add(a: i32, b: i32) -> i32 { a + b }

let x: Labeled<i32, Public, Trusted> = Labeled::new(2);
let y: Labeled<i32, Secret, Trusted> = Labeled::new(40);
let sum = labeled_call!(add(x, y)); // Labeled<i32, Secret, Trusted>

let s: Labeled<String, Secret, Trusted> = Labeled::new("hello".into());
let len = labeled_get!(s.len()); // Labeled<usize, Secret, Trusted>

Arithmetic and comparisons

The standard arithmetic and bitwise operators (+, -, *, /, %, &, |, ^, <<, >>, unary -, !) work on Labeled values. The result label is the join of the operand labels.

PartialEq is not implemented because it would return a plain bool, stripping the label. Use labeled_eq, labeled_ne, labeled_lt, labeled_le, labeled_gt, labeled_ge instead.

use sifr::{labeled_eq, Labeled, Public, Secret, Trusted};

let a: Labeled<i32, Public, Trusted> = Labeled::new(1);
let b: Labeled<i32, Secret, Trusted> = Labeled::new(1);
let eq: Labeled<bool, Secret, Trusted> = labeled_eq(&a, &b);

Debug and Display

Debug and Display are implemented only when C: CanFlowTo<Public>. Formatting a value whose confidentiality label is Internal or Secret is a compile error.

About

Compile-time information flow control for Rust. Labels sensitive values at the type level; the compiler rejects unsafe flows.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages