ibex/rtl/ibex_pointer_authentication.sv
2020-07-15 17:02:42 +02:00

193 lines
6 KiB
Systemverilog

// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
module ibex_pointer_authentication #(
// This parameter is used in PAC instruction to rewrite MSBs of pointer to an invalid value
parameter logic [3:0] MSBsInvalidPointer = 4'b1010
) (
input logic clk_i,
input logic rst_ni,
input logic [127:0] csr_pa_key_i,
input logic pac_en_i,
input logic aut_en_i,
input logic [31:0] pa_data0_i,
input logic [31:0] pa_data1_i,
input logic pa_ready_id_i,
output logic [31:0] pa_result_o,
output logic pa_valid_o
);
logic [3:0] msbs_pointer_q, msbs_pointer_d;
// We could theoretically avoid to buffer the pac
// by changing the decoder to provide the following outputs:
// Cycle 1: ptr MSBs (just store ptr MSBs, ignore the pac coming from same register)
// Cycle 2: ptr LSBs + context (now we can start the cipher)
// Cycle 3 onwards: pac
// But this is not currently done as it requires a lot of changes.
logic [27:0] pac_q, pac_d;
logic [63:0] cipher_in_data;
logic cipher_in_ready;
logic cipher_in_valid;
logic [31:0] cipher_out_data;
logic cipher_out_ready;
logic cipher_out_valid;
ibex_cipher ibex_cipher_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.key_i ( csr_pa_key_i ),
.in_data_i ( cipher_in_data ),
.in_valid_i ( cipher_in_valid ),
.in_ready_o ( cipher_in_ready ),
.out_data_o ( cipher_out_data ),
.out_valid_o ( cipher_out_valid ),
.out_ready_i ( cipher_out_ready )
);
typedef enum logic [1:0] {
PA_IDLE, PA_START_AUT, PA_WAIT
} pa_fsm_e;
pa_fsm_e pa_fsm_q, pa_fsm_d;
typedef enum logic [1:0] {
PAC_START, PAC_DONE, AUT_SUCCESS, AUT_FAILURE
} pa_result_sel_e;
pa_result_sel_e pa_result_sel;
typedef enum logic [1:0] {
CIPHER_IDLE, CIPHER_PAC_START, CIPHER_AUT_START
} cipher_in_data_sel_e;
cipher_in_data_sel_e cipher_in_data_sel;
always_comb begin : pa_result_mux
unique case (pa_result_sel)
// MSBs of invalid pointer, LSBs of pointer
PAC_START: pa_result_o = {MSBsInvalidPointer, pa_data0_i[27:0]};
// MSBs of pointer, Pointer Authentication Code
PAC_DONE: pa_result_o = {msbs_pointer_q, cipher_out_data[27:0]};
// MSBs of pointer, LSB of pointer
AUT_SUCCESS: pa_result_o = {msbs_pointer_q, pa_data0_i[27:0]};
// We should make sure that the invalid bits get filled in the pointer
// no matter the content of msbs_pointer_q
// MSBs of invalid pointer, LSBs of pointer
AUT_FAILURE: pa_result_o = {MSBsInvalidPointer, pa_data0_i[27:0]};
// We usually take the first value from above,
// but AUT_FAILURE seems to be more sensible in this scenario
default: pa_result_o = {MSBsInvalidPointer, pa_data0_i[27:0]};
endcase
end
always_comb begin : cipher_in_data_mux
unique case (cipher_in_data_sel)
CIPHER_IDLE: cipher_in_data = '0;
// context, MSBs of pointer, LSBs of pointer
CIPHER_PAC_START: cipher_in_data = {pa_data1_i, msbs_pointer_d, pa_data0_i[27:0]};
CIPHER_AUT_START: cipher_in_data = {pa_data1_i, msbs_pointer_q, pa_data0_i[27:0]};
endcase
end
// FSM
always_comb begin
pa_result_sel = AUT_FAILURE;
pa_valid_o = '0;
msbs_pointer_d = msbs_pointer_q;
pac_d = pac_q;
cipher_in_data_sel = CIPHER_IDLE;
cipher_in_valid = '0;
cipher_out_ready = '0;
pa_fsm_d = pa_fsm_q;
unique case (pa_fsm_q)
PA_IDLE: begin
unique case (1'b1)
pac_en_i: begin
pa_result_sel = PAC_START;
msbs_pointer_d = pa_data0_i[31:28];
cipher_in_data_sel = CIPHER_PAC_START;
cipher_in_valid = 1'b1;
if (cipher_in_ready) begin
pa_fsm_d = PA_WAIT;
end
end
aut_en_i: begin
msbs_pointer_d = pa_data1_i[31:28];
pac_d = pa_data1_i[27:0];
pa_fsm_d = PA_START_AUT;
end
default: begin
pa_fsm_d = PA_IDLE;
end
endcase
end
PA_START_AUT: begin
cipher_in_data_sel = CIPHER_AUT_START;
cipher_in_valid = 1'b1;
if (cipher_in_ready) begin
pa_fsm_d = PA_WAIT;
end
end
PA_WAIT: begin
unique case (1'b1)
pac_en_i: begin
if (pa_ready_id_i) begin
cipher_out_ready = 1'b1;
end
if (cipher_out_ready && cipher_out_valid) begin
pa_result_sel = PAC_DONE;
pa_valid_o = 1'b1;
pa_fsm_d = PA_IDLE;
end
end
aut_en_i: begin
if (pa_ready_id_i) begin
cipher_out_ready = 1'b1;
end
if (cipher_out_ready && cipher_out_valid) begin
if (pac_q == cipher_out_data[27:0]) begin // Authentication success
pa_result_sel = AUT_SUCCESS;
end else begin // Authentication failure
pa_result_sel = AUT_FAILURE;
end
pa_valid_o = 1'b1;
pa_fsm_d = PA_IDLE;
end
end
default: begin
pa_fsm_d = PA_IDLE;
end
endcase
end
default: begin
pa_fsm_d = PA_IDLE;
end
endcase
end
// registers for FSM
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
pa_fsm_q <= PA_IDLE;
msbs_pointer_q <= '0;
pac_q <= '0;
end else begin
pa_fsm_q <= pa_fsm_d;
msbs_pointer_q <= msbs_pointer_d;
pac_q <= pac_d;
end
end
endmodule