Concurrent Leakage Variants (NuSMV)¶
Description of the Case Study¶
The concurrent leakage suite models how different schedulers and input manipulations influence information flow between three
processes that communicate via shared Boolean registers x and y. The attacker observes the outputs generated by the
second process. We analyse three variants: a baseline with two processes, a three-process refinement that preserves security,
and a buggy three-process model where the third process forces low inputs to leak the secret. All instances are encoded as
flattened NuSMV models that explicitly capture the scheduler and loop counters.
The NuSMV model(s)¶
MODULE main
VAR
    -- globals (unchanged)
    in_HIGH : boolean;    -- in_HIGH-value
    LOW     : boolean;    -- low-value
    var_X   : boolean;    -- X
    var_Y   : boolean;    -- Y
    SCHEDULE : 0..1;      -- 0: p1 moves, 1: p2 moves
    ID_one   : 0..2;
    ID_two   : 0..2;
    ID_three : 0..2;
    -- halt : boolean;
    -- flattened state for proc1: programs(ID_one, SCHEDULE, LOW, in_HIGH, var_X, var_Y, 0)
    proc1_line       : 0..8;
    proc1_loop_count : 0..2;
    -- flattened state for proc2: programs(ID_two, SCHEDULE, LOW, in_HIGH, var_X, var_Y, 6)
    proc2_line       : 0..8;
    proc2_loop_count : 0..2;
ASSIGN
    ----------------------------------------------------------------
    -- IDs (as in your original)
    ----------------------------------------------------------------
    init(ID_one) := 0;
    next(ID_one) := 0;
    init(ID_two) := 1;
    next(ID_two) := 1;
    ----------------------------------------------------------------
    -- inputs / other globals (updated references)
    ----------------------------------------------------------------
    init(in_HIGH) := {TRUE, FALSE};
    next(in_HIGH) :=
        case
            TRUE : in_HIGH;
        esac;
    init(LOW) := FALSE;
    next(LOW) :=
        case
            TRUE : LOW;
        esac;
    init(var_X) := FALSE;
    next(var_X) :=
        case
            (proc1_line = 0)              : {FALSE};
            (proc1_line = 2 | proc1_line = 5) : {TRUE};
            TRUE                          : var_X;
        esac;
    init(var_Y) := FALSE;
    next(var_Y) :=
        case
            (proc1_line = 0)              : {FALSE};
            (proc1_line = 3 | proc1_line = 3) : {TRUE}; -- (same as your original)
            TRUE                          : var_Y;
        esac;
    init(SCHEDULE) := {0,1};
    next(SCHEDULE) :=
        case
            (proc1_loop_count = 2) : 1;
            (proc2_loop_count = 2) : 0;
            TRUE                   : {0,1};
        esac;
    -- init(halt) := FALSE;
    -- next(halt) :=
    --     case
    --         ((proc1_loop_count = 2) & (proc2_loop_count = 2)) : TRUE;
    --         TRUE                                              : halt;
    --     esac;
    ----------------------------------------------------------------
    -- proc1: programs(ID_one, SCHEDULE, LOW, in_HIGH, var_X, var_Y, 0) — flattened
    ----------------------------------------------------------------
    init(proc1_loop_count) := 0;
    next(proc1_loop_count) :=
        case
            (proc1_loop_count = 2)                          : 2;
            (ID_one = 0 & proc1_line = 0 & (SCHEDULE = ID_one)) : proc1_loop_count + 1;
            (ID_one = 1 & proc1_line = 6 & (SCHEDULE = ID_one)) : proc1_loop_count + 1;
            TRUE                                             : proc1_loop_count;
        esac;
    init(proc1_line) := 0;
    next(proc1_line) :=
        case
            (!(SCHEDULE = ID_one)) : proc1_line;
            -- program1
            (proc1_line = 0)                         : {1};         -- x=0, y=0
            (proc1_line = 1 & in_HIGH = LOW)         : {2};         -- if (h == l)
            (proc1_line = 2)                         : {3};         -- x=1
            (proc1_line = 3)                         : {0};         -- y=1
            (proc1_line = 1 & !(in_HIGH = LOW))      : {4};
            (proc1_line = 4)                         : {5};         -- y=1
            (proc1_line = 5)                         : {0};         -- x=1
            -- program2
            (proc1_line = 6)                         : {7};
            (proc1_line = 7)                         : {8};         -- output x
            (proc1_line = 8)                         : {6};         -- output y
            TRUE : proc1_line;
        esac;
    ----------------------------------------------------------------
    -- proc2: programs(ID_two, SCHEDULE, LOW, in_HIGH, var_X, var_Y, 6) — flattened
    ----------------------------------------------------------------
    init(proc2_loop_count) := 0;
    next(proc2_loop_count) :=
        case
            (proc2_loop_count = 2)                          : 2;
            (ID_two = 0 & proc2_line = 0 & (SCHEDULE = ID_two)) : proc2_loop_count + 1;
            (ID_two = 1 & proc2_line = 6 & (SCHEDULE = ID_two)) : proc2_loop_count + 1;
            TRUE                                             : proc2_loop_count;
        esac;
    init(proc2_line) := 6;
    next(proc2_line) :=
        case
            (!(SCHEDULE = ID_two)) : proc2_line;
            -- program1
            (proc2_line = 0)                         : {1};         -- x=0, y=0
            (proc2_line = 1 & in_HIGH = LOW)         : {2};         -- if (h == l)
            (proc2_line = 2)                         : {3};         -- x=1
            (proc2_line = 3)                         : {0};         -- y=1
            (proc2_line = 1 & !(in_HIGH = LOW))      : {4};
            (proc2_line = 4)                         : {5};         -- y=1
            (proc2_line = 5)                         : {0};         -- x=1
            -- program2
            (proc2_line = 6)                         : {7};
            (proc2_line = 7)                         : {8};         -- output x
            (proc2_line = 8)                         : {6};         -- output y
            TRUE : proc2_line;
        esac;
DEFINE
    -- observables (updated to flattened names)
    obs_X_is_zero := (proc2_line = 7) & (in_HIGH = FALSE);
    obs_X_is_one  := (proc2_line = 7) & (in_HIGH = TRUE);
    obs_Y_is_zero := (proc2_line = 8) & (in_HIGH = FALSE);
    obs_Y_is_one  := (proc2_line = 8) & (in_HIGH = TRUE);
    -- halting condition
    halt := ((proc1_line=8) & (proc2_line=8));
MODULE main
  VAR
    -- top-level shared state
    in_HIGH   : boolean;       -- in_HIGH-value
    in_LOW    : boolean;       -- in_LOW-value
    var_X     : boolean;       -- X
    var_Y     : boolean;       -- Y
    SCHEDULE  : 0..2;          -- 0:p1 moves, 1:p2 moves, 2:p3 moves
    ID_one    : 0..2;
    ID_two    : 0..2;
    ID_three  : 0..2;
    -- inlined programs(ID_one, SCHEDULE, ..., initline=1)  as proc1
    proc1_line        : 0..12;
    proc1_loop_count  : 0..2;
    -- inlined programs(ID_two, SCHEDULE, ..., initline=6)  as proc2
    proc2_line        : 0..12;
    proc2_loop_count  : 0..2;
    -- inlined programs(ID_three, SCHEDULE, ..., initline=9) as proc3
    proc3_line        : 0..12;
    proc3_loop_count  : 0..2;
  ASSIGN
    -- IDs are fixed to 0,1,2
    init(ID_one)   := 0;
    next(ID_one)   := 0;
    init(ID_two)   := 1;
    next(ID_two)   := 1;
    init(ID_three) := 2;
    next(ID_three) := 2;
    -- inputs toggled by proc3
    init(in_HIGH) := FALSE;
    next(in_HIGH) :=
      case
        (proc3_line = 10) : {TRUE, FALSE};
        TRUE              : in_HIGH;
      esac;
    init(in_LOW) := FALSE;
    next(in_LOW) :=
      case
        (proc3_line = 11) : {TRUE, FALSE};
        TRUE              : in_LOW;
      esac;
    -- X updated by proc1
    init(var_X) := FALSE;
    next(var_X) :=
      case
        (proc1_line = 0)               : {FALSE};
        (proc1_line = 2 | proc1_line = 5) : {TRUE};
        TRUE                           : var_X;
      esac;
    -- Y updated by proc1
    init(var_Y) := FALSE;
    next(var_Y) :=
      case
        (proc1_line = 0)               : {FALSE};
        (proc1_line = 3 | proc1_line = 4) : {TRUE};
        TRUE                           : var_Y;
      esac;
    -- round-robin-ish schedule updates after each program finishes 2 loops
    init(SCHEDULE) := {0, 1, 2};
    next(SCHEDULE) :=
      case
        (proc1_loop_count = 2) : {1, 2};
        (proc2_loop_count = 2) : {0, 2};
        (proc3_loop_count = 2) : {0, 1};
        TRUE                   : SCHEDULE;
      esac;
    ----------------------------------------------------------------------------
    -- Inlined programs(ID_one, SCHEDULE, in_LOW, in_HIGH, var_X, var_Y, initline=1)
    ----------------------------------------------------------------------------
    init(proc1_loop_count) := 0;
    next(proc1_loop_count) :=
      case
        (proc1_loop_count = 2)                           : 2;
        (proc1_line = 0 & (SCHEDULE = ID_one))           : proc1_loop_count + 1;
        TRUE                                             : proc1_loop_count;
      esac;
    init(proc1_line) := 1;
    next(proc1_line) :=
      case
        -- scheduler gate: if not this program’s turn, it stutters
        !(SCHEDULE = ID_one)                                 : proc1_line;
        -- program 1 (lines 0..5)
        (proc1_line = 0)                                     : {1};                   -- x=0, y=0
        (proc1_line = 1 & (in_HIGH = in_LOW))                : {2};                   -- if (h == l)
        (proc1_line = 2)                                     : {3};                   -- x=1
        (proc1_line = 3)                                     : {0};                   -- y=1
        (proc1_line = 1 & !(in_HIGH = in_LOW))               : {4};
        (proc1_line = 4)                                     : {5};                   -- y=1
        (proc1_line = 5)                                     : {0};                   -- x=1
        -- program 2 (lines 6..8)
        (proc1_line = 6)                                     : {7};
        (proc1_line = 7)                                     : {8};                   -- output x
        (proc1_line = 8)                                     : {6};                   -- output y
        -- program 3 (lines 9..11)
        (proc1_line = 9)                                     : {10};
        (proc1_line = 10)                                    : {11};                  -- in_HIGH := {0,1} at top-level
        (proc1_line = 11)                                    : {9};                   -- in_LOW  := {0,1}  at top-level
        TRUE                                                 : proc1_line;
      esac;
    ----------------------------------------------------------------------------
    -- Inlined programs(ID_two, SCHEDULE, in_LOW, in_HIGH, var_X, var_Y, initline=6)
    ----------------------------------------------------------------------------
    init(proc2_loop_count) := 0;
    next(proc2_loop_count) :=
      case
        (proc2_loop_count = 2)                           : 2;
        (proc2_line = 6 & (SCHEDULE = ID_two))           : proc2_loop_count + 1;
        TRUE                                             : proc2_loop_count;
      esac;
    init(proc2_line) := 6;
    next(proc2_line) :=
      case
        !(SCHEDULE = ID_two)                                 : proc2_line;
        (proc2_line = 0)                                     : {1};
        (proc2_line = 1 & (in_HIGH = in_LOW))                : {2};
        (proc2_line = 2)                                     : {3};
        (proc2_line = 3)                                     : {0};
        (proc2_line = 1 & !(in_HIGH = in_LOW))               : {4};
        (proc2_line = 4)                                     : {5};
        (proc2_line = 5)                                     : {0};
        (proc2_line = 6)                                     : {7};
        (proc2_line = 7)                                     : {8};
        (proc2_line = 8)                                     : {6};
        (proc2_line = 9)                                     : {10};
        (proc2_line = 10)                                    : {11};
        (proc2_line = 11)                                    : {9};
        TRUE                                                 : proc2_line;
      esac;
    ----------------------------------------------------------------------------
    -- Inlined programs(ID_three, SCHEDULE, in_LOW, in_HIGH, var_X, var_Y, initline=9)
    ----------------------------------------------------------------------------
    init(proc3_loop_count) := 0;
    next(proc3_loop_count) :=
      case
        (proc3_loop_count = 2)                           : 2;
        (proc3_line = 9 & (SCHEDULE = ID_three))         : proc3_loop_count + 1;
        TRUE                                             : proc3_loop_count;
      esac;
    init(proc3_line) := 9;
    next(proc3_line) :=
      case
        !(SCHEDULE = ID_three)                               : proc3_line;
        (proc3_line = 0)                                     : {1};
        (proc3_line = 1 & (in_HIGH = in_LOW))                : {2};
        (proc3_line = 2)                                     : {3};
        (proc3_line = 3)                                     : {0};
        (proc3_line = 1 & !(in_HIGH = in_LOW))               : {4};
        (proc3_line = 4)                                     : {5};
        (proc3_line = 5)                                     : {0};
        (proc3_line = 6)                                     : {7};
        (proc3_line = 7)                                     : {8};
        (proc3_line = 8)                                     : {6};
        (proc3_line = 9)                                     : {10};
        (proc3_line = 10)                                    : {11};
        (proc3_line = 11)                                    : {9};
        TRUE                                                 : proc3_line;
      esac;
  DEFINE
    -- observables (now refer to inlined proc2 state)
    obs_X_is_zero := ((proc2_line = 7) & (var_X = FALSE));
    obs_X_is_one  := ((proc2_line = 7) & (var_X = TRUE));
    obs_Y_is_zero := ((proc2_line = 8) & (var_Y = FALSE));
    obs_Y_is_one  := ((proc2_line = 8) & (var_Y = TRUE));
    -- global halt: each inlined program finished two loops
    halt := (proc1_loop_count = 2 & proc2_loop_count = 2 & proc3_loop_count = 2);
MODULE main
  VAR
    -- top-level shared state
    HIGH     : boolean;     -- high-value
    LOW      : boolean;     -- low-value
    var_X    : boolean;     -- X
    var_Y    : boolean;     -- Y
    SCHEDULE : 0..2;        -- 0:p1 moves, 1:p2 moves, 2:p3 moves
    ID_one   : 0..2;
    ID_two   : 0..2;
    ID_three : 0..2;
    -- inlined programs(ID_one, SCHEDULE, LOW, HIGH, var_X, var_Y, initline=1) as proc1
    proc1_line : 0..12;
    -- inlined programs(ID_two, SCHEDULE, LOW, HIGH, var_X, var_Y, initline=6) as proc2
    proc2_line : 0..12;
    -- inlined programs(ID_three, SCHEDULE, LOW, HIGH, var_X, var_Y, initline=9) as proc3
    proc3_line : 0..12;
  ASSIGN
    -- IDs are fixed
    init(ID_one)   := 0;
    next(ID_one)   := 0;
    init(ID_two)   := 1;
    next(ID_two)   := 1;
    init(ID_three) := 2;
    next(ID_three) := 2;
    -- inputs toggled by proc3
    init(HIGH) := FALSE;
    next(HIGH) :=
      case
        (proc3_line = 10) : {TRUE, FALSE};
        TRUE              : HIGH;
      esac;
    init(LOW) := FALSE;
    next(LOW) :=
      case
        (proc3_line = 11) : {TRUE};  -- sequence LOW: 0 -> 1
        TRUE              : LOW;
      esac;
    -- X/Y updated by proc1
    init(var_X) := FALSE;
    next(var_X) :=
      case
        (proc1_line = 0)                    : {FALSE};
        (proc1_line = 2 | proc1_line = 5)   : {TRUE};
        TRUE                                : var_X;
      esac;
    init(var_Y) := FALSE;
    next(var_Y) :=
      case
        (proc1_line = 0)                    : {FALSE};
        (proc1_line = 3 | proc1_line = 4)   : {TRUE};
        TRUE                                : var_Y;
      esac;
    --------------------------------------------------------------------------
    -- Inlined programs(..., initline=1)  -> proc1_line
    --------------------------------------------------------------------------
    init(proc1_line) := 1;
    next(proc1_line) :=
      case
        -- scheduler gate: if not this program’s turn, it stutters
        !(SCHEDULE = ID_one)                         : proc1_line;
        -- program1 (lines 0..5)
        (proc1_line = 0)                             : {1};        -- x=0, y=0
        (proc1_line = 1 & (HIGH = LOW))              : {2};        -- if (h == l)
        (proc1_line = 2)                             : {3};        -- x=1
        (proc1_line = 3)                             : {0};        -- y=1
        (proc1_line = 1 & !(HIGH = LOW))             : {4};
        (proc1_line = 4)                             : {5};        -- y=1
        (proc1_line = 5)                             : {0};        -- x=1
        -- program2 (lines 6..8)
        (proc1_line = 6)                             : {7};
        (proc1_line = 7)                             : {8};        -- output x
        (proc1_line = 8)                             : {6};        -- output y
        -- program3 (lines 9..11)
        (proc1_line = 9)                             : {10};
        (proc1_line = 10)                            : {11};       -- HIGH := {0,1} at top level
        (proc1_line = 11)                            : {9};        -- LOW  := 1 at top level (per your case)
        TRUE                                         : proc1_line;
      esac;
    --------------------------------------------------------------------------
    -- Inlined programs(..., initline=6)  -> proc2_line
    --------------------------------------------------------------------------
    init(proc2_line) := 6;
    next(proc2_line) :=
      case
        !(SCHEDULE = ID_two)                         : proc2_line;
        (proc2_line = 0)                             : {1};
        (proc2_line = 1 & (HIGH = LOW))              : {2};
        (proc2_line = 2)                             : {3};
        (proc2_line = 3)                             : {0};
        (proc2_line = 1 & !(HIGH = LOW))             : {4};
        (proc2_line = 4)                             : {5};
        (proc2_line = 5)                             : {0};
        (proc2_line = 6)                             : {7};
        (proc2_line = 7)                             : {8};        -- output x
        (proc2_line = 8)                             : {6};        -- output y
        (proc2_line = 9)                             : {10};
        (proc2_line = 10)                            : {11};
        (proc2_line = 11)                            : {9};
        TRUE                                         : proc2_line;
      esac;
    --------------------------------------------------------------------------
    -- Inlined programs(..., initline=9)  -> proc3_line
    --------------------------------------------------------------------------
    init(proc3_line) := 9;
    next(proc3_line) :=
      case
        !(SCHEDULE = ID_three)                       : proc3_line;
        (proc3_line = 0)                             : {1};
        (proc3_line = 1 & (HIGH = LOW))              : {2};
        (proc3_line = 2)                             : {3};
        (proc3_line = 3)                             : {0};
        (proc3_line = 1 & !(HIGH = LOW))             : {4};
        (proc3_line = 4)                             : {5};
        (proc3_line = 5)                             : {0};
        (proc3_line = 6)                             : {7};
        (proc3_line = 7)                             : {8};
        (proc3_line = 8)                             : {6};
        (proc3_line = 9)                             : {10};
        (proc3_line = 10)                            : {11};       -- HIGH := {0,1} at top level
        (proc3_line = 11)                            : {9};        -- LOW  := 1 at top level
        TRUE                                         : proc3_line;
      esac;
  DEFINE
    -- the observables (now referencing inlined proc2)
    obs_X_is_zero := ((proc2_line = 7) & (var_X = FALSE));
    obs_X_is_one  := ((proc2_line = 7) & (var_X = TRUE));
    obs_Y_is_zero := ((proc2_line = 8) & (var_Y = FALSE));
    obs_Y_is_one  := ((proc2_line = 8) & (var_Y = TRUE));
    -- halt condition from original DEFINE
    halt := ((proc1_line = 5) & (proc2_line = 8) & (proc3_line = 11));
The HyperLTL formula¶
All variants are checked against the same observational determinism property. As long as the high and low inputs agree across
traces, the observable events obs_X_is_zero, obs_X_is_one, obs_Y_is_zero, and obs_Y_is_one must coincide. The buggy
model violates this requirement because the forced low input reveals the secret.
Forall A . Exists B . E t .  
G(
    (in_HIGH[A][t] = in_HIGH[B][t]) 
)
-> 
G( 
    (obs_X_is_zero[A][t] = obs_X_is_zero[B][t]) &       
    (obs_X_is_one[A][t] = obs_X_is_one[B][t]) &
    (obs_Y_is_zero[A][t] = obs_Y_is_zero[B][t]) &
    (obs_Y_is_one[A][t] = obs_Y_is_one[B][t]) 
)