Botan 3.11.1
Crypto and TLS for C&
whirlpool.cpp
Go to the documentation of this file.
1/*
2* Whirlpool
3* (C) 1999-2007,2020,2026 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/whirlpool.h>
9
10#include <botan/internal/bit_ops.h>
11#include <botan/internal/buffer_slicer.h>
12#include <botan/internal/loadstor.h>
13#include <botan/internal/rotate.h>
14#include <array>
15
16#if defined(BOTAN_HAS_CPUID)
17 #include <botan/internal/cpuid.h>
18#endif
19
20namespace Botan {
21
22namespace {
23
24// Derive the 256-byte S-box from the Whirlpool E and R mini-boxes
25consteval std::array<uint8_t, 256> whirlpool_sbox() noexcept {
26 constexpr uint8_t Ebox[16] = {1, 11, 9, 12, 13, 6, 15, 3, 14, 8, 7, 4, 10, 2, 5, 0};
27 constexpr uint8_t Rbox[16] = {7, 12, 11, 13, 14, 4, 9, 15, 6, 3, 8, 10, 2, 5, 1, 0};
28
29 // Derive the inverse of the E table
30 uint8_t Eibox[16] = {};
31 for(size_t i = 0; i != 16; ++i) {
32 Eibox[Ebox[i]] = static_cast<uint8_t>(i);
33 }
34
35 std::array<uint8_t, 256> S = {};
36 for(size_t i = 0; i != 256; ++i) {
37 const uint8_t L = Ebox[i >> 4];
38 const uint8_t R = Eibox[i & 0x0F];
39 const uint8_t T = Rbox[L ^ R];
40 S[i] = static_cast<uint8_t>((Ebox[L ^ T] << 4) | Eibox[R ^ T]);
41 }
42 return S;
43}
44
45// Combined S-box + MDS diffusion table
46consteval std::array<uint64_t, 256> whirlpool_T_table(const std::array<uint8_t, 256>& S) noexcept {
47 // MDS circulant matrix first row: [1, 1, 4, 1, 8, 5, 2, 9] over GF(2^8)
48 constexpr uint64_t MDS = 0x0101040108050209;
49
50 std::array<uint64_t, 256> T = {};
51 for(size_t i = 0; i != 256; ++i) {
52 T[i] = poly_mul<0x1D>(MDS, S[i]);
53 }
54 return T;
55}
56
57// Round constants are from the first 64 elements of the sbox
58consteval std::array<uint64_t, 10> whirlpool_rc(const std::array<uint8_t, 256>& S) noexcept {
59 std::array<uint64_t, 10> RC = {};
60 for(size_t r = 0; r != 10; ++r) {
61 RC[r] = load_be<uint64_t>(S.data(), r);
62 }
63 return RC;
64}
65
66constexpr auto WHIRL_S = whirlpool_sbox();
67alignas(256) constexpr auto WHIRL_T = whirlpool_T_table(WHIRL_S);
68constexpr auto WHIRL_RC = whirlpool_rc(WHIRL_S);
69
70uint64_t whirl(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7) {
71 const uint64_t s0 = WHIRL_T[get_byte<0>(x0)];
72 const uint64_t s1 = WHIRL_T[get_byte<1>(x1)];
73 const uint64_t s2 = WHIRL_T[get_byte<2>(x2)];
74 const uint64_t s3 = WHIRL_T[get_byte<3>(x3)];
75 const uint64_t s4 = WHIRL_T[get_byte<4>(x4)];
76 const uint64_t s5 = WHIRL_T[get_byte<5>(x5)];
77 const uint64_t s6 = WHIRL_T[get_byte<6>(x6)];
78 const uint64_t s7 = WHIRL_T[get_byte<7>(x7)];
79
80 return s0 ^ rotr<8>(s1) ^ rotr<16>(s2) ^ rotr<24>(s3) ^ rotr<32>(s4) ^ rotr<40>(s5) ^ rotr<48>(s6) ^ rotr<56>(s7);
81}
82
83} // namespace
84
85std::string Whirlpool::provider() const {
86#if defined(BOTAN_HAS_WHIRLPOOL_AVX512)
87 if(auto feat = CPUID::check(CPUID::Feature::AVX512)) {
88 return *feat;
89 }
90#endif
91
92#if defined(BOTAN_HAS_WHIRLPOOL_AVX2)
93 if(auto feat = CPUID::check(CPUID::Feature::AVX2)) {
94 return *feat;
95 }
96#endif
97
98 return "base";
99}
100
101/*
102* Whirlpool Compression Function
103*/
104void Whirlpool::compress_n(digest_type& digest, std::span<const uint8_t> input, size_t blocks) {
105#if defined(BOTAN_HAS_WHIRLPOOL_AVX512)
107 return compress_n_avx512(digest, input, blocks);
108 }
109#endif
110
111#if defined(BOTAN_HAS_WHIRLPOOL_AVX2)
113 return compress_n_avx2(digest, input, blocks);
114 }
115#endif
116
117 BufferSlicer in(input);
118
119 for(size_t i = 0; i != blocks; ++i) {
120 const auto block = in.take(block_bytes);
121
122 uint64_t K[11 * 8] = {0};
123
124 K[0] = digest[0];
125 K[1] = digest[1];
126 K[2] = digest[2];
127 K[3] = digest[3];
128 K[4] = digest[4];
129 K[5] = digest[5];
130 K[6] = digest[6];
131 K[7] = digest[7];
132
133 // Whirlpool key schedule:
134 for(size_t r = 1; r != 11; ++r) {
135 const uint64_t PK0 = K[8 * (r - 1) + 0];
136 const uint64_t PK1 = K[8 * (r - 1) + 1];
137 const uint64_t PK2 = K[8 * (r - 1) + 2];
138 const uint64_t PK3 = K[8 * (r - 1) + 3];
139 const uint64_t PK4 = K[8 * (r - 1) + 4];
140 const uint64_t PK5 = K[8 * (r - 1) + 5];
141 const uint64_t PK6 = K[8 * (r - 1) + 6];
142 const uint64_t PK7 = K[8 * (r - 1) + 7];
143
144 K[8 * r + 0] = whirl(PK0, PK7, PK6, PK5, PK4, PK3, PK2, PK1) ^ WHIRL_RC[r - 1];
145 K[8 * r + 1] = whirl(PK1, PK0, PK7, PK6, PK5, PK4, PK3, PK2);
146 K[8 * r + 2] = whirl(PK2, PK1, PK0, PK7, PK6, PK5, PK4, PK3);
147 K[8 * r + 3] = whirl(PK3, PK2, PK1, PK0, PK7, PK6, PK5, PK4);
148 K[8 * r + 4] = whirl(PK4, PK3, PK2, PK1, PK0, PK7, PK6, PK5);
149 K[8 * r + 5] = whirl(PK5, PK4, PK3, PK2, PK1, PK0, PK7, PK6);
150 K[8 * r + 6] = whirl(PK6, PK5, PK4, PK3, PK2, PK1, PK0, PK7);
151 K[8 * r + 7] = whirl(PK7, PK6, PK5, PK4, PK3, PK2, PK1, PK0);
152 }
153
154 uint64_t M[8] = {0};
155 load_be(M, block.data(), 8);
156
157 // First round (key masking)
158 uint64_t B0 = M[0] ^ K[0];
159 uint64_t B1 = M[1] ^ K[1];
160 uint64_t B2 = M[2] ^ K[2];
161 uint64_t B3 = M[3] ^ K[3];
162 uint64_t B4 = M[4] ^ K[4];
163 uint64_t B5 = M[5] ^ K[5];
164 uint64_t B6 = M[6] ^ K[6];
165 uint64_t B7 = M[7] ^ K[7];
166
167 for(size_t r = 1; r != 11; ++r) {
168 const uint64_t T0 = whirl(B0, B7, B6, B5, B4, B3, B2, B1) ^ K[8 * r + 0];
169 const uint64_t T1 = whirl(B1, B0, B7, B6, B5, B4, B3, B2) ^ K[8 * r + 1];
170 const uint64_t T2 = whirl(B2, B1, B0, B7, B6, B5, B4, B3) ^ K[8 * r + 2];
171 const uint64_t T3 = whirl(B3, B2, B1, B0, B7, B6, B5, B4) ^ K[8 * r + 3];
172 const uint64_t T4 = whirl(B4, B3, B2, B1, B0, B7, B6, B5) ^ K[8 * r + 4];
173 const uint64_t T5 = whirl(B5, B4, B3, B2, B1, B0, B7, B6) ^ K[8 * r + 5];
174 const uint64_t T6 = whirl(B6, B5, B4, B3, B2, B1, B0, B7) ^ K[8 * r + 6];
175 const uint64_t T7 = whirl(B7, B6, B5, B4, B3, B2, B1, B0) ^ K[8 * r + 7];
176
177 B0 = T0;
178 B1 = T1;
179 B2 = T2;
180 B3 = T3;
181 B4 = T4;
182 B5 = T5;
183 B6 = T6;
184 B7 = T7;
185 }
186
187 digest[0] ^= B0 ^ M[0];
188 digest[1] ^= B1 ^ M[1];
189 digest[2] ^= B2 ^ M[2];
190 digest[3] ^= B3 ^ M[3];
191 digest[4] ^= B4 ^ M[4];
192 digest[5] ^= B5 ^ M[5];
193 digest[6] ^= B6 ^ M[6];
194 digest[7] ^= B7 ^ M[7];
195 }
196}
197
199 digest.resize(8);
200 zeroise(digest);
201}
202
203std::unique_ptr<HashFunction> Whirlpool::new_object() const {
204 return std::make_unique<Whirlpool>();
205}
206
207std::unique_ptr<HashFunction> Whirlpool::copy_state() const {
208 return std::make_unique<Whirlpool>(*this);
209}
210
211void Whirlpool::add_data(std::span<const uint8_t> input) {
212 m_md.update(input);
213}
214
215void Whirlpool::final_result(std::span<uint8_t> output) {
216 m_md.final(output);
217}
218
219} // namespace Botan
std::span< const uint8_t > take(const size_t count)
static std::optional< std::string > check(CPUID::Feature feat)
Definition cpuid.h:67
static bool has(CPUID::Feature feat)
Definition cpuid.h:94
std::string provider() const override
Definition whirlpool.cpp:85
std::unique_ptr< HashFunction > new_object() const override
static void compress_n(digest_type &digest, std::span< const uint8_t > input, size_t blocks)
static void init(digest_type &digest)
secure_vector< uint64_t > digest_type
Definition whirlpool.h:20
std::unique_ptr< HashFunction > copy_state() const override
static constexpr size_t block_bytes
Definition whirlpool.h:24
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:124
BOTAN_FORCE_INLINE constexpr T rotr(T input)
Definition rotate.h:35
constexpr T poly_mul(T x, uint8_t y)
Definition bit_ops.h:306
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504