/* * (C) 2026 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include 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 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 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