|  | // 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 // | 
|  | ////////////// | 
|  |  | 
|  | logic [NumRounds:0][DataWidth-1:0] data_state; | 
|  |  | 
|  | // 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 | 
|  | 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_dec | 
|  | 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. | 
|  | 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 |