mirror of
https://github.com/YosysHQ/sby.git
synced 2025-04-07 06:44:06 +00:00
279 lines
6.7 KiB
Systemverilog
279 lines
6.7 KiB
Systemverilog
// Simple sync FIFO implementation using an extra bit in the read and write
|
|
// pointers to distinguish the completely full and completely empty case.
|
|
module fifo #(
|
|
DEPTH_BITS = 4,
|
|
WIDTH = 8
|
|
) (
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
input wire in_valid,
|
|
input wire [WIDTH-1:0] in_data,
|
|
output reg in_ready,
|
|
|
|
output reg out_valid,
|
|
output reg [WIDTH-1:0] out_data,
|
|
input wire out_ready
|
|
);
|
|
|
|
reg [WIDTH-1:0] buffer [1<<DEPTH_BITS];
|
|
|
|
reg [DEPTH_BITS:0] write_addr;
|
|
reg [DEPTH_BITS:0] read_addr;
|
|
|
|
wire in_transfer = in_valid && in_ready;
|
|
wire out_transfer = out_valid && out_ready;
|
|
|
|
wire [DEPTH_BITS:0] write_limit = read_addr ^ (1 << DEPTH_BITS);
|
|
|
|
assign in_ready = write_addr != write_limit;
|
|
assign out_valid = read_addr != write_addr;
|
|
|
|
assign out_data = buffer[read_addr[DEPTH_BITS-1:0]];
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
read_addr <= 0;
|
|
write_addr <= 0;
|
|
end else begin
|
|
if (in_transfer) begin
|
|
buffer[write_addr[DEPTH_BITS-1:0]] <= in_data;
|
|
write_addr <= write_addr + 1'b1;
|
|
end
|
|
|
|
if (out_transfer) begin
|
|
read_addr <= read_addr + 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
// This module sends some data and should not be able to receive any
|
|
// information back, even under the assumption that it can trick the other side
|
|
// into performing unintended operations that would attempt to establish a
|
|
// covert channel.
|
|
module untrusted_module (
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
input wire en,
|
|
|
|
output reg out_valid,
|
|
output reg [31:0] out_data,
|
|
input wire out_ready
|
|
);
|
|
|
|
reg [31:0] lfsr;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
lfsr <= '1;
|
|
end else if (en && out_valid && out_ready) begin
|
|
lfsr <= (lfsr << 1) ^ (lfsr[31] ? 32'h04C11DB7 : 0);
|
|
end
|
|
end
|
|
|
|
assign out_valid = out_ready && en;
|
|
assign out_data = lfsr;
|
|
|
|
endmodule
|
|
|
|
// This module receives data and contains secret data. It should guard this
|
|
// secret data even if there are bugs in this module's implementations that the
|
|
// other side could exploit in an attempt to establish a covert channel to
|
|
// exfiltrate the secrets.
|
|
module secret_storing_module (
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
input wire en,
|
|
output reg [31:0] counter,
|
|
|
|
input wire in_valid,
|
|
input wire [31:0] in_data,
|
|
output reg in_ready
|
|
);
|
|
|
|
reg [3:0] busy;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
counter <= '0;
|
|
busy <= '0;
|
|
end else if (en) begin
|
|
if (busy) begin
|
|
busy <= busy - 4'b1;
|
|
end else if (in_valid && in_ready) begin
|
|
busy <= counter[3:0];
|
|
counter <= counter + in_data;
|
|
end
|
|
end
|
|
end
|
|
|
|
assign in_ready = en && !busy;
|
|
|
|
endmodule
|
|
|
|
|
|
// This data diode wraps a FIFO, but ensures that there is no backwards data
|
|
// flow via the handshaking signals. Instead it limits the sender to send with
|
|
// a fixed rate and only notifies the receiver
|
|
module data_diode #(
|
|
DEPTH_BITS = 4,
|
|
WIDTH = 8,
|
|
RATE = 2
|
|
) (
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
input wire in_valid,
|
|
input wire [WIDTH-1:0] in_data,
|
|
output reg in_ready,
|
|
|
|
output reg out_valid,
|
|
output reg [WIDTH-1:0] out_data,
|
|
input wire out_ready,
|
|
|
|
output reg overflow
|
|
);
|
|
reg [$clog2(RATE):0] delay;
|
|
wire fifo_ready;
|
|
|
|
always @(posedge clk) begin
|
|
if (rst) begin
|
|
delay <= 0;
|
|
overflow <= 0;
|
|
end else begin
|
|
if (in_valid && in_ready) begin
|
|
if (!fifo_ready) begin
|
|
overflow <= 1;
|
|
end
|
|
delay <= RATE - 1;
|
|
end else if (delay) begin
|
|
delay <= delay - 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
assign in_ready = !delay;
|
|
|
|
fifo #(.DEPTH_BITS(DEPTH_BITS), .WIDTH(WIDTH)) fifo (
|
|
.clk(clk),
|
|
.rst(rst),
|
|
|
|
.in_valid(in_valid),
|
|
.in_data(in_data),
|
|
.in_ready(fifo_ready),
|
|
|
|
.out_valid(out_valid),
|
|
.out_data(out_data),
|
|
.out_ready(out_ready)
|
|
);
|
|
|
|
endmodule
|
|
|
|
// The top module either connects the untrusted and secret storing modules
|
|
// directly or via the data diode.
|
|
module top(
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
input wire source_en,
|
|
input wire sink_en,
|
|
|
|
`ifdef USE_DIODE
|
|
output reg overflow,
|
|
`endif
|
|
output reg [31:0] counter
|
|
|
|
);
|
|
|
|
wire source_valid;
|
|
wire [31:0] source_data;
|
|
wire source_ready;
|
|
|
|
wire sink_valid;
|
|
wire [31:0] sink_data;
|
|
wire sink_ready;
|
|
|
|
untrusted_module source (
|
|
.clk(clk),
|
|
.rst(rst),
|
|
.en(source_en),
|
|
|
|
.out_valid(source_valid),
|
|
.out_data(source_data),
|
|
.out_ready(source_ready)
|
|
);
|
|
|
|
`ifdef USE_DIODE
|
|
data_diode #(.WIDTH(32)) data_diode (
|
|
.clk(clk),
|
|
.rst(rst),
|
|
|
|
.in_valid(source_valid),
|
|
.in_data(source_data),
|
|
.in_ready(source_ready),
|
|
|
|
.out_valid(sink_valid),
|
|
.out_data(sink_data),
|
|
.out_ready(sink_ready),
|
|
|
|
.overflow(overflow)
|
|
);
|
|
`else
|
|
fifo #(.WIDTH(32)) fifo (
|
|
.clk(clk),
|
|
.rst(rst),
|
|
|
|
.in_valid(source_valid),
|
|
.in_data(source_data),
|
|
.in_ready(source_ready),
|
|
|
|
.out_valid(sink_valid),
|
|
.out_data(sink_data),
|
|
.out_ready(sink_ready)
|
|
);
|
|
`endif
|
|
|
|
secret_storing_module sink (
|
|
.clk(clk),
|
|
.rst(rst),
|
|
.en(sink_en),
|
|
|
|
.in_valid(sink_valid),
|
|
.in_data(sink_data),
|
|
.in_ready(sink_ready),
|
|
|
|
.counter(counter)
|
|
);
|
|
endmodule
|
|
|
|
// We can inject a tag into signals of a module by binding a checker that
|
|
// contains an `$overwrite_tag` item.
|
|
checker mark_secret(counter);
|
|
$overwrite_tag("secret", counter);
|
|
endchecker
|
|
|
|
bind secret_storing_module mark_secret counter_is_secret(counter);
|
|
|
|
// We cen inject a check for a tag by binding a checker that has an assertion
|
|
// that uses `$get_tag`.
|
|
//
|
|
// When using a direct connection, this assertion will fail as there is a
|
|
// covert channel via the FIFO's handshaking signals. When the data-diode is
|
|
// used, we are able to prove the absance of such a covert channel.
|
|
checker leak_check(clk, signal);
|
|
secret_leaked: assert property (@(posedge clk) !$get_tag("secret", signal));
|
|
endchecker
|
|
|
|
bind untrusted_module leak_check leak_check(clk, out_ready);
|
|
|
|
|
|
checker initial_reset(clk, rst);
|
|
assume property (@(posedge clk) $initstate |-> rst);
|
|
endchecker
|
|
|
|
bind top initial_reset initial_reset(clk, rst);
|