| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // This is a simple data diffusion primitive that is constructed in a similar fashion |
| // as the PRESENT cipher (i.e. it uses a substitution/permutation network). Note however |
| // that this is **not** cryptographically secure. The main purpose of this primitive is to |
| // provide a cheap diffusion mechanism for arbitrarily sized vectors. |
| // |
| // See also: prim_prince, prim_present, prim_cipher_pkg |
| |
| module prim_subst_perm #( |
| parameter int DataWidth = 64, |
| parameter int NumRounds = 31, |
| parameter bit Decrypt = 0 // 0: encrypt, 1: decrypt |
| ) ( |
| input [DataWidth-1:0] data_i, |
| input [DataWidth-1:0] key_i, |
| output logic [DataWidth-1:0] data_o |
| ); |
| |
| ////////////// |
| // datapath // |
| ////////////// |
| |
| // The "split_var" hint that we pass to verilator here tells it to schedule the different parts of |
| // data_state separately. This avoids an UNOPTFLAT error where it would otherwise see a dependency |
| // chain |
| // |
| // data_state -> data_state_sbox -> data_state |
| // |
| logic [NumRounds:0][DataWidth-1:0] data_state /* verilator split_var */; |
| |
| // initialize |
| assign data_state[0] = data_i; |
| |
| for (genvar r = 0; r < NumRounds; r++) begin : gen_round |
| logic [DataWidth-1:0] data_state_sbox, data_state_flipped; |
| //////////////////////////////// |
| // decryption pass, performs inverse permutation and sbox |
| if (Decrypt) begin : gen_dec |
| always_comb begin : p_dec |
| data_state_sbox = data_state[r] ^ key_i; |
| // Reverse odd/even grouping |
| data_state_flipped = data_state_sbox; |
| for (int k = 0; k < DataWidth/2; k++) begin |
| data_state_flipped[k * 2] = data_state_sbox[k]; |
| data_state_flipped[k * 2 + 1] = data_state_sbox[k + DataWidth/2]; |
| end |
| // Flip vector |
| for (int k = 0; k < DataWidth; k++) begin |
| data_state_sbox[DataWidth - 1 - k] = data_state_flipped[k]; |
| end |
| // Inverse SBox layer |
| for (int k = 0; k < DataWidth/4; k++) begin |
| data_state_sbox[k*4 +: 4] = prim_cipher_pkg::PRESENT_SBOX4_INV[data_state_sbox[k*4 +: 4]]; |
| end |
| data_state[r + 1] = data_state_sbox; |
| end |
| //////////////////////////////// |
| // encryption pass |
| end else begin : gen_enc |
| always_comb begin : p_enc |
| data_state_sbox = data_state[r] ^ key_i; |
| // This SBox layer is aligned to nibbles, so the uppermost bits may not be affected by this. |
| // However, the permutation below ensures that these bits get shuffled to a different |
| // position when performing multiple rounds. |
| for (int k = 0; k < DataWidth/4; k++) begin |
| data_state_sbox[k*4 +: 4] = prim_cipher_pkg::PRESENT_SBOX4[data_state_sbox[k*4 +: 4]]; |
| end |
| // Flip the vector to move the MSB positions into the LSB positions |
| for (int k = 0; k < DataWidth; k++) begin |
| data_state_flipped[DataWidth - 1 - k] = data_state_sbox[k]; |
| end |
| // Regroup bits such that all even indices are stacked up first, followed by all odd |
| // indices. Note that if the Width is odd, this is still ok, since |
| // the uppermost bit just stays in place in that case. |
| data_state_sbox = data_state_flipped; |
| for (int k = 0; k < DataWidth/2; k++) begin |
| data_state_sbox[k] = data_state_flipped[k * 2]; |
| data_state_sbox[k + DataWidth/2] = data_state_flipped[k * 2 + 1]; |
| end |
| data_state[r + 1] = data_state_sbox; |
| end |
| end // gen_enc |
| //////////////////////////////// |
| end // gen_round |
| |
| // finalize |
| assign data_o = data_state[NumRounds] ^ key_i; |
| |
| endmodule : prim_subst_perm |