This crate implements a type system for L~equivalence IFC with a delimited release declassifier. Full documentation for the project exists in the docs
folder.
In cargo.toml
, add the ifc
and ifc-macros
to dependencies
[dependencies]
ifc = {git = "https://github.com/oddcoder/ifc"}
ifc-macros = {git = "https://github.com/oddcoder/ifc"}
The project should work with stable rust. However, at the time of writing this readme, Rust nightly is required to get detailed error messages.
This example will compile sucessfully:
fn main() {
ifc_block!{
let a = 5;
if a == 5 {
let x = 5;
x
} else {
let x = 6;
x
};
}
}
However, the following example fails:
fn main() {
ifc_block!{
let a = 5;
if a == 5 {
let x = 5;
x
} else {
#[IFC(Low)]
let x = 6;
x
};
}
}
The reported error is :
error: Cannot declare new low variable or assign to low variable in high context
--> src\main.rs:12:13
|
12 | let x = 6;
| ^^^^^^^^^^
|
help: High context was created here
--> src\main.rs:7:12
|
7 | if a == 5 {
| ^^^^^^
help: Low variable is used here.
--> src\main.rs:12:17
|
12 | let x = 6;
| ^
To create a context where IFC rules apply, use the macro ifc_block
as follows:
ifc_block!{
// This is a code block where IFC rules apply.
}
From here one everything we mention goes inside ifc_block macro. By default, all variables are high unless specified otherwise. Low variables require a specific marker using rust attributes as follows:
let this_is_high_varialble_by_default;
#[IFC(Low)]
let this_is_low_varialble;
#[IFC(High)] // this one is not necessary since it is high by default.
let another_high_variable;
The following IFC holds:
- You can assign low variables to low variables or high variables.
- You can assign high variables only to another high variable.
- You may assign high variables to low variables if they are attributed with
#[IFC(Declassify)]
. - Loops, if statements, match statements with a high variable in the condition must only assign to high variables in the body.
- Function calls return value is always a low variable (due to limited functions support).
- Arguments to function calls cannot be high variables unless the function is attributed with
#[IFC(Unsafe)]
.
- Let statements (e.g.
let x = 5
). - Assign expressions (e.g.
x = 5
). - Assign-op experessions (e.g.
x += 5
). - Binary Operations (e.g.
x + y
). - Unary Operations (e.g.
-x
). - Nested blocks (e.g.
{let x = 5; {let x = 5}}
). - Function calls (e.g.
foobar(x,y,z)
). - If statements {e.g.
if condition {...} else {...}
}. - Literals (e.g.
"foobar"
). - Macros (e.g.
println!("hello world")
). - Match statements (e.g.
match x {_ => ()}
). - Parens (e.g.
(x)
). - While loops {e.g.
while x {}
}. - Arrays {e.g.
[1,2,3,4, x]
}. - Async Expressions {e.g.
async {...}
}. - Await Expression (e.g.
fut.await
). - Break statement.
- Type casting (e.g.
a as u8
). - Closures. (e.g.
|e| e + 1
). - Continue statement.
- Fields (e.g.
foo.bar
). - For loops (e.g
for i in 0..5 {...}
) - Infinite loops (e.g
loop {}
). - Method calls (e.g
foo.bar()
). - Ranges (e.g
0..5
). - Structs, Enums, and function definitions.
- Full Modules.