-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcastella-permute.hpp
More file actions
206 lines (181 loc) · 5.62 KB
/
Copy pathcastella-permute.hpp
File metadata and controls
206 lines (181 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// SPDX-FileCopyrightText: Steven Ward
// SPDX-License-Identifier: MPL-2.0
/// Castella round constants and permutation function
/**
* \file
* \author Steven Ward
*/
#pragma once
#include "aes_enc.hpp"
#include "simd_load.hpp"
#include "simd_transpose.hpp"
#include "simd_types.hpp"
#if defined(DEBUG)
#include <cassert>
#endif
#include <cstddef>
#include <ranges>
#include <span>
namespace Castella
{
using block_t = uint8x16_t;
template <size_t N>
using arr_blocks = simd_arr_t<N>;
/// The minimum number of rounds for \c aes_enc_0 to achieve full bit diffusion
/**
* The value was obtained from research/aes_enc_0-aes_num_rounds.cpp
*
* ## _JDA_VRI_Rijndael_2002.pdf_
* ### 3.5 The Number of Rounds
* #### Page 41 (56)
*
* <blockquote>
* Two rounds of Rijndael provide 'full diffusion' in the following sense: every
* state bit depends on all state bits two rounds ago, or a change in one state
* bit is likely to affect half of the state bits after two rounds.
* </blockquote>
* \sa https://crypto.stackexchange.com/questions/44532/how-2-rounds-in-aes-achieve-full-diffusion
*/
inline constexpr int AES_NUM_ROUNDS = 3;
/// For state size \a N, get the minimum number of rounds for \c Castella::permute to achieve full bit diffusion
/**
* The values were obtained from research/permute-num_rounds.cpp and
* corroborated by research/permute-num_rounds-avalanche_matrix.cpp
*/
template <int N>
requires (N == 2) || (N == 4) || (N == 8) || (N == 16)
consteval int
NUM_ROUNDS_MIN()
{
switch (N)
{
case 2:
case 4:
case 8:
return 2;
case 16:
return 3;
default:
break;
}
}
// Embiggen the value as needed.
inline constexpr int NUM_ROUNDS_MAX = 16;
static_assert(NUM_ROUNDS_MIN<2>() <= NUM_ROUNDS_MAX);
static_assert(NUM_ROUNDS_MIN<4>() <= NUM_ROUNDS_MAX);
static_assert(NUM_ROUNDS_MIN<8>() <= NUM_ROUNDS_MAX);
static_assert(NUM_ROUNDS_MIN<16>() <= NUM_ROUNDS_MAX);
/// Create the first \a N Castella round constants
/**
* The first Castella round constant is <q>expand 16-byte c</q>.
* Each subsequent Castella round constant is generated by performing 1 round of
* AES encryption (with a zero round key) on the preceding one.
*/
template <size_t N>
[[nodiscard]] static auto
create_round_constants() noexcept
{
static_assert(N > 0);
arr_blocks<N> result;
// It's a perfectly cromulent initial value.
const auto rc_0 = simd_load16("expand 16-byte c");
result[0] = rc_0;
for (int i = 1; i < std::ssize(result); ++i)
{
result[i] = aes_enc_0(result[i - 1]);
}
return result;
}
/// The Castella round constants
// {{{
/**
* ## _MakingOfKeccak.pdf_
*
* ### 7.4 The hermetic sponge strategy
* #### Page 21
*
* <blockquote>
* There needs to be some asymmetry between the rounds to avoid slide attacks.
* This can be addressed by including the addition of round constants that differ
* from round to round to the state. These constants may also provide asymmetry
* to the round function to avoid symmetric properties (see Section 8).
* </blockquote>
*
* ### 8.7 The round constants
* #### Page 27
*
* <blockquote>
* The round constants are there to disrupt symmetry, both in the temporal as in
* the three spatial dimensions. … The bits of the round constants are different
* from round to round and are taken as the output of a maximum-length eight-bit
* linear feedback shift register.
* </blockquote>
*/
// }}}
inline const auto round_constants = create_round_constants<NUM_ROUNDS_MAX>();
/// The Castella permutation function
// {{{
/**
* \param state the state to permute
* \param num_rounds the number of rounds to perform
* \pre \a N ∈ {2, 4, 8, 16}
* \pre \a num_rounds ≥ \c 0
* \pre \a num_rounds ≤ \c NUM_ROUNDS_MAX
* Each round consists of the following steps:
* 1. Apply (via XOR) the round constant to the first element of the state array.
* 2. Perform \c AES_NUM_ROUNDS rounds of AES encryption (with a zero round key) on each
* element of the state array.
* 3. Transpose the state, treating it as a _NxN_ matrix of _(128/N)-bit_
* integers.
*/
// }}}
template <size_t N>
static void
permute(arr_blocks<N>& state, const int num_rounds) noexcept
{
static_assert((N == 2) || (N == 4) || (N == 8) || (N == 16));
#if defined(DEBUG)
assert(num_rounds >= 0);
assert(num_rounds <= NUM_ROUNDS_MAX);
#endif
for (const auto& rc : std::span{round_constants}.first(num_rounds))
{
state.front() ^= rc;
aes_enc_0_arr<AES_NUM_ROUNDS>(state);
simd_transpose(state);
}
}
/// The inverse Castella permutation function
// {{{
/**
* \param state the state to permute
* \param num_rounds the number of rounds to perform
* \pre \a N ∈ {2, 4, 8, 16}
* \pre \a num_rounds ≥ \c 0
* \pre \a num_rounds ≤ \c NUM_ROUNDS_MAX
* Rounds are performed in reverse order, and each round consists of the following
* steps (in reverse order of \c permute):
* 1. Transpose the state, treating it as a _NxN_ matrix of _(128/N)-bit_
* integers.
* 2. Perform \c AES_NUM_ROUNDS inverse rounds of AES encryption (with a zero round key) on
* each element of the state array.
* 3. Apply (via XOR) the round constant to the first element of the state array.
*/
// }}}
template <size_t N>
static void
permute_inv(arr_blocks<N>& state, const int num_rounds) noexcept
{
static_assert((N == 2) || (N == 4) || (N == 8) || (N == 16));
#if defined(DEBUG)
assert(num_rounds >= 0);
assert(num_rounds <= NUM_ROUNDS_MAX);
#endif
for (const auto& rc : std::span{round_constants}.first(num_rounds) | std::views::reverse)
{
simd_transpose(state);
aes_enc_0_inv_arr<AES_NUM_ROUNDS>(state);
state.front() ^= rc;
}
}
} // namespace Castella