Revision control

Copy as Markdown

Other Tools

/*
* (C) 2026 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#include <botan/internal/seed.h>
#include <botan/mem_ops.h>
#include <botan/internal/isa_extn.h>
#include <botan/internal/simd_4x32.h>
#include <botan/internal/simd_hwaes.h>
namespace Botan {
namespace SEED_HWAES {
namespace {
BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES SIMD_4x32 seed_g(SIMD_4x32 X) {
// Field isomorphism from SEED's field (0x163) to AES field (0x11B)
constexpr uint64_t pre_a = gfni_matrix(R"(
1 1 0 1 0 0 0 0
0 0 1 1 0 0 1 1
0 0 0 0 1 1 0 1
0 1 1 1 0 1 0 0
0 1 1 0 1 0 0 0
0 0 0 1 1 0 0 0
0 0 1 1 1 1 0 0
0 0 0 0 1 1 1 0)");
// AES->SEED field isomorphism composed with S0's affine
constexpr uint64_t s0_post_a = gfni_matrix(R"(
0 1 0 1 1 0 0 1
0 0 1 1 1 0 1 0
1 0 0 0 1 1 1 0
1 1 0 0 1 0 0 1
0 1 0 1 1 0 1 1
1 1 1 1 1 0 1 1
0 0 1 1 0 1 0 1
0 0 0 1 0 1 1 1)");
constexpr uint8_t s0_post_c = 0xA9;
// AES->SEED field isomorphism composed with S1's affine
constexpr uint64_t s1_post_a = gfni_matrix(R"(
0 0 1 1 0 1 1 0
0 1 1 0 0 0 1 0
0 1 0 1 1 0 1 1
0 0 0 0 0 0 1 1
1 1 0 1 0 0 0 0
0 1 0 0 1 0 1 1
1 1 1 0 1 0 1 1
1 1 1 1 0 0 0 1)");
constexpr uint8_t s1_post_c = 0x38;
constexpr auto pre = Gf2AffineTransformation(pre_a, 0x00);
constexpr auto post_s0 = Gf2AffineTransformation::post_sbox(s0_post_a, s0_post_c);
constexpr auto post_s1 = Gf2AffineTransformation::post_sbox(s1_post_a, s1_post_c);
// Shared computation for S0(x) and S1(x)
const auto sub = hw_aes_sbox(pre.affine_transform(X));
// Compute S0(x) and S1(x)
const auto s0 = post_s0.affine_transform(sub);
const auto s1 = post_s1.affine_transform(sub);
// Blend S0(x) and S1(x) outputs in alternating bytes
const auto sbox = SIMD_4x32::byte_blend(0x00FF00FF, s0, s1);
// Linear mixing step
const auto M0 = SIMD_4x32::splat(0x3FCFF3FC);
const auto M1 = SIMD_4x32::splat(0xFC3FCFF3);
const auto M2 = SIMD_4x32::splat(0xF3FC3FCF);
const auto M3 = SIMD_4x32::splat(0xCFF3FC3F);
// Broadcast each byte of a 32-bit word to all 4 positions
const auto SHUF0 = SIMD_4x32(0x00000000, 0x04040404, 0x08080808, 0x0C0C0C0C);
const auto SHUF1 = SIMD_4x32(0x01010101, 0x05050505, 0x09090909, 0x0D0D0D0D);
const auto SHUF2 = SIMD_4x32(0x02020202, 0x06060606, 0x0A0A0A0A, 0x0E0E0E0E);
const auto SHUF3 = SIMD_4x32(0x03030303, 0x07070707, 0x0B0B0B0B, 0x0F0F0F0F);
auto b0 = SIMD_4x32::byte_shuffle(sbox, SHUF0);
auto b1 = SIMD_4x32::byte_shuffle(sbox, SHUF1);
auto b2 = SIMD_4x32::byte_shuffle(sbox, SHUF2);
auto b3 = SIMD_4x32::byte_shuffle(sbox, SHUF3);
return (b0 & M0) ^ (b1 & M1) ^ (b2 & M2) ^ (b3 & M3);
}
BOTAN_FORCE_INLINE BOTAN_FN_ISA_HWAES void seed_round(
SIMD_4x32& B0, SIMD_4x32& B1, SIMD_4x32& B2, SIMD_4x32& B3, uint32_t K0, uint32_t K1, uint32_t K2, uint32_t K3) {
auto T0 = B2 ^ SIMD_4x32::splat(K0);
auto T1 = seed_g(B2 ^ B3 ^ SIMD_4x32::splat(K1));
T0 = seed_g(T1 + T0);
T1 = seed_g(T1 + T0);
B1 ^= T1;
B0 ^= T0 + T1;
T0 = B0 ^ SIMD_4x32::splat(K2);
T1 = seed_g(B0 ^ B1 ^ SIMD_4x32::splat(K3));
T0 = seed_g(T1 + T0);
T1 = seed_g(T1 + T0);
B3 ^= T1;
B2 ^= T0 + T1;
}
BOTAN_FN_ISA_HWAES void encrypt_4(const uint8_t ptext[4 * 16], uint8_t ctext[4 * 16], std::span<const uint32_t> RK) {
auto B0 = SIMD_4x32::load_be(ptext);
auto B1 = SIMD_4x32::load_be(ptext + 16);
auto B2 = SIMD_4x32::load_be(ptext + 32);
auto B3 = SIMD_4x32::load_be(ptext + 48);
SIMD_4x32::transpose(B0, B1, B2, B3);
for(size_t j = 0; j != 8; ++j) {
const uint32_t K0 = RK[4 * j];
const uint32_t K1 = RK[4 * j + 1];
const uint32_t K2 = RK[4 * j + 2];
const uint32_t K3 = RK[4 * j + 3];
seed_round(B0, B1, B2, B3, K0, K1, K2, K3);
}
// Output order: B2, B3, B0, B1
SIMD_4x32::transpose(B2, B3, B0, B1);
B2.store_be(ctext);
B3.store_be(ctext + 16);
B0.store_be(ctext + 32);
B1.store_be(ctext + 48);
}
BOTAN_FN_ISA_HWAES void decrypt_4(const uint8_t ctext[4 * 16], uint8_t ptext[4 * 16], std::span<const uint32_t> RK) {
auto B0 = SIMD_4x32::load_be(ctext);
auto B1 = SIMD_4x32::load_be(ctext + 16);
auto B2 = SIMD_4x32::load_be(ctext + 32);
auto B3 = SIMD_4x32::load_be(ctext + 48);
SIMD_4x32::transpose(B0, B1, B2, B3);
for(size_t j = 0; j != 8; ++j) {
const uint32_t K0 = RK[30 - 4 * j];
const uint32_t K1 = RK[31 - 4 * j];
const uint32_t K2 = RK[28 - 4 * j];
const uint32_t K3 = RK[29 - 4 * j];
seed_round(B0, B1, B2, B3, K0, K1, K2, K3);
}
SIMD_4x32::transpose(B2, B3, B0, B1);
B2.store_be(ptext);
B3.store_be(ptext + 16);
B0.store_be(ptext + 32);
B1.store_be(ptext + 48);
}
} // namespace
} // namespace SEED_HWAES
void BOTAN_FN_ISA_HWAES SEED::hwaes_encrypt(const uint8_t ptext[], uint8_t ctext[], size_t blocks) const {
while(blocks >= 4) {
SEED_HWAES::encrypt_4(ptext, ctext, m_K);
ptext += 4 * 16;
ctext += 4 * 16;
blocks -= 4;
}
if(blocks > 0) {
uint8_t pbuf[4 * 16] = {0};
uint8_t cbuf[4 * 16] = {0};
copy_mem(pbuf, ptext, blocks * 16);
SEED_HWAES::encrypt_4(pbuf, cbuf, m_K);
copy_mem(ctext, cbuf, blocks * 16);
}
}
void BOTAN_FN_ISA_HWAES SEED::hwaes_decrypt(const uint8_t ctext[], uint8_t ptext[], size_t blocks) const {
while(blocks >= 4) {
SEED_HWAES::decrypt_4(ctext, ptext, m_K);
ptext += 4 * 16;
ctext += 4 * 16;
blocks -= 4;
}
if(blocks > 0) {
uint8_t cbuf[4 * 16] = {0};
uint8_t pbuf[4 * 16] = {0};
copy_mem(cbuf, ctext, blocks * 16);
SEED_HWAES::decrypt_4(cbuf, pbuf, m_K);
copy_mem(ptext, pbuf, blocks * 16);
}
}
} // namespace Botan