From 5d80fe151d0184523d8ade37856881cd91ece543 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 26 Jul 2026 10:56:54 -0400 Subject: [PATCH 1/2] Add Salsa20 SIMD implementations Basically mirroring the approach used for ChaCha, with generic 128-bit SIMD (SSSE3/NEON/etc), AVX2, and AVX-512 implementations. On Intel Tiger Lake, improves Salsa20 write_keystream cycles per byte results by up to 9x: | Impl | CPB | | -------- | ---- | | Baseline | 3.92 | | SSSE3 | 1.88 | | AVX2 | .95 | | AVX512 | .42 | --- doc/dev_ref/todo.rst | 1 - src/lib/stream/salsa20/salsa20.cpp | 132 +++++++++++++++--- src/lib/stream/salsa20/salsa20.h | 18 +++ src/lib/stream/salsa20/salsa20_avx2/info.txt | 17 +++ .../salsa20/salsa20_avx2/salsa20_avx2.cpp | 128 +++++++++++++++++ .../stream/salsa20/salsa20_avx512/info.txt | 17 +++ .../salsa20/salsa20_avx512/salsa20_avx512.cpp | 131 +++++++++++++++++ .../stream/salsa20/salsa20_simd32/info.txt | 25 ++++ .../salsa20/salsa20_simd32/salsa20_simd32.cpp | 128 +++++++++++++++++ src/tests/data/stream/chacha.vec | 2 +- src/tests/data/stream/salsa20.vec | 28 ++++ src/tests/test_stream.cpp | 19 +++ 12 files changed, 625 insertions(+), 21 deletions(-) create mode 100644 src/lib/stream/salsa20/salsa20_avx2/info.txt create mode 100644 src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp create mode 100644 src/lib/stream/salsa20/salsa20_avx512/info.txt create mode 100644 src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp create mode 100644 src/lib/stream/salsa20/salsa20_simd32/info.txt create mode 100644 src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp diff --git a/doc/dev_ref/todo.rst b/doc/dev_ref/todo.rst index b7a83ab1292..0fc03106059 100644 --- a/doc/dev_ref/todo.rst +++ b/doc/dev_ref/todo.rst @@ -26,7 +26,6 @@ Hardware Specific Optimizations * GFNI implementations of ZFEC, others? * NEON/VMX/LSX support for the SIMD based GHASH * SIMD evaluation of SHA-2 and SHA-3 compression functions -* Improved Salsa implementations (SIMD_4x32, AVX2, AVX512, ...) * Add CLMUL/PMULL implementations for CRC24 * Add support for ARMv8.4-A SHA-3 instructions * Support POWER8 SHA-2 extensions (GH #1486 + #1487) diff --git a/src/lib/stream/salsa20/salsa20.cpp b/src/lib/stream/salsa20/salsa20.cpp index 69046078eac..30718f3e0ad 100644 --- a/src/lib/stream/salsa20/salsa20.cpp +++ b/src/lib/stream/salsa20/salsa20.cpp @@ -11,6 +11,10 @@ #include #include +#if defined(BOTAN_HAS_CPUID) + #include +#endif + namespace Botan { namespace { @@ -122,6 +126,88 @@ void Salsa20::salsa_core(uint8_t output[64], const uint32_t input[16], size_t ro store_le(x15 + input[15], output + 4 * 15); } +size_t Salsa20::parallelism() { +#if defined(BOTAN_HAS_SALSA20_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + return 16; + } +#endif + +#if defined(BOTAN_HAS_SALSA20_AVX2) + if(CPUID::has(CPUID::Feature::AVX2)) { + return 8; + } +#endif + + return 4; +} + +std::string Salsa20::provider() const { +#if defined(BOTAN_HAS_SALSA20_AVX512) + if(auto feat = CPUID::check(CPUID::Feature::AVX512)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SALSA20_AVX2) + if(auto feat = CPUID::check(CPUID::Feature::AVX2)) { + return *feat; + } +#endif + +#if defined(BOTAN_HAS_SALSA20_SIMD32) + if(auto feat = CPUID::check(CPUID::Feature::SIMD_4X32)) { + return *feat; + } +#endif + + return "base"; +} + +//static +void Salsa20::salsa20(uint8_t output[], size_t output_blocks, uint32_t state[16], size_t rounds) { + BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); + +#if defined(BOTAN_HAS_SALSA20_AVX512) + if(CPUID::has(CPUID::Feature::AVX512)) { + while(output_blocks >= 16) { + Salsa20::salsa20_avx512_x16(output, state, rounds); + output += 16 * 64; + output_blocks -= 16; + } + } +#endif + +#if defined(BOTAN_HAS_SALSA20_AVX2) + if(CPUID::has(CPUID::Feature::AVX2)) { + while(output_blocks >= 8) { + Salsa20::salsa20_avx2_x8(output, state, rounds); + output += 8 * 64; + output_blocks -= 8; + } + } +#endif + +#if defined(BOTAN_HAS_SALSA20_SIMD32) + if(CPUID::has(CPUID::Feature::SIMD_4X32)) { + while(output_blocks >= 4) { + Salsa20::salsa20_simd32_x4(output, state, rounds); + output += 4 * 64; + output_blocks -= 4; + } + } +#endif + + for(size_t i = 0; i != output_blocks; ++i) { + salsa_core(output + 64 * i, state, rounds); + + ++state[8]; + if(state[8] == 0) { + state[9] += 1; + } + } +} + /* * Combine cipher stream with message */ @@ -132,12 +218,7 @@ void Salsa20::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) { const size_t available = m_buffer.size() - m_position; xor_buf(out, in, &m_buffer[m_position], available); - salsa_core(m_buffer.data(), m_state.data(), 20); - - ++m_state[8]; - if(m_state[8] == 0) { - m_state[9] += 1; - } + salsa20(m_buffer.data(), m_buffer.size() / 64, m_state.data(), 20); length -= available; in += available; @@ -151,6 +232,27 @@ void Salsa20::cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) { m_position += length; } +void Salsa20::generate_keystream(uint8_t out[], size_t length) { + assert_key_material_set(); + + while(length >= m_buffer.size() - m_position) { + const size_t available = m_buffer.size() - m_position; + + // TODO: this could write directly to the output buffer + // instead of bouncing it through m_buffer first + copy_mem(out, &m_buffer[m_position], available); + salsa20(m_buffer.data(), m_buffer.size() / 64, m_state.data(), 20); + + length -= available; + out += available; + m_position = 0; + } + + copy_mem(out, &m_buffer[m_position], length); + + m_position += length; +} + void Salsa20::initialize_state() { static const uint32_t TAU[] = {0x61707865, 0x3120646e, 0x79622d36, 0x6b206574}; @@ -205,7 +307,9 @@ void Salsa20::key_schedule(std::span key) { load_le(m_key.data(), key.data(), m_key.size()); m_state.resize(16); - m_buffer.resize(64); + + const size_t salsa_block = 64; + m_buffer.resize(parallelism() * salsa_block); set_iv(nullptr, 0); } @@ -255,12 +359,7 @@ void Salsa20::set_iv_bytes(const uint8_t iv[], size_t length) { m_state[8] = 0; m_state[9] = 0; - salsa_core(m_buffer.data(), m_state.data(), 20); - ++m_state[8]; - if(m_state[8] == 0) { - m_state[9] += 1; - } - + salsa20(m_buffer.data(), m_buffer.size() / 64, m_state.data(), 20); m_position = 0; } @@ -302,12 +401,7 @@ void Salsa20::seek(uint64_t offset) { m_state[8] = static_cast(counter); m_state[9] = static_cast(counter >> 32); - salsa_core(m_buffer.data(), m_state.data(), 20); - - ++m_state[8]; - if(m_state[8] == 0) { - m_state[9] += 1; - } + salsa20(m_buffer.data(), m_buffer.size() / 64, m_state.data(), 20); m_position = offset % 64; } diff --git a/src/lib/stream/salsa20/salsa20.h b/src/lib/stream/salsa20/salsa20.h index 542c2f6dfab..88ba1a2ba2c 100644 --- a/src/lib/stream/salsa20/salsa20.h +++ b/src/lib/stream/salsa20/salsa20.h @@ -17,6 +17,7 @@ namespace Botan { */ class Salsa20 final : public StreamCipher { public: + std::string provider() const override; bool valid_iv_length(size_t iv_len) const override; size_t default_iv_length() const override; Key_Length_Specification key_spec() const override; @@ -41,6 +42,7 @@ class Salsa20 final : public StreamCipher { protected: void cipher_bytes(const uint8_t in[], uint8_t out[], size_t length) override; + void generate_keystream(uint8_t out[], size_t len) override; void set_iv_bytes(const uint8_t iv[], size_t iv_len) override; private: @@ -48,6 +50,22 @@ class Salsa20 final : public StreamCipher { void initialize_state(); + static size_t parallelism(); + + static void salsa20(uint8_t output[], size_t output_blocks, uint32_t state[16], size_t rounds); + +#if defined(BOTAN_HAS_SALSA20_SIMD32) + static void salsa20_simd32_x4(uint8_t output[64 * 4], uint32_t state[16], size_t rounds); +#endif + +#if defined(BOTAN_HAS_SALSA20_AVX2) + static void salsa20_avx2_x8(uint8_t output[64 * 8], uint32_t state[16], size_t rounds); +#endif + +#if defined(BOTAN_HAS_SALSA20_AVX512) + static void salsa20_avx512_x16(uint8_t output[64 * 16], uint32_t state[16], size_t rounds); +#endif + secure_vector m_key; secure_vector m_state; secure_vector m_buffer; diff --git a/src/lib/stream/salsa20/salsa20_avx2/info.txt b/src/lib/stream/salsa20/salsa20_avx2/info.txt new file mode 100644 index 00000000000..759a6becb25 --- /dev/null +++ b/src/lib/stream/salsa20/salsa20_avx2/info.txt @@ -0,0 +1,17 @@ + +SALSA20_AVX2 -> 20260726 + + + +name -> "Salsa20 AVX2" +brief -> "Salsa20 using AVX2 instructions" + + + +avx2 + + + +simd_avx2 +cpuid + diff --git a/src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp b/src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp new file mode 100644 index 00000000000..5335f8ca736 --- /dev/null +++ b/src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp @@ -0,0 +1,128 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +//static +void BOTAN_FN_ISA_AVX2 Salsa20::salsa20_avx2_x8(uint8_t output[64 * 8], uint32_t state[16], size_t rounds) { + SIMD_8x32::reset_registers(); + + BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); + const SIMD_8x32 CTR0 = SIMD_8x32(0, 1, 2, 3, 4, 5, 6, 7); + + const uint32_t C = 0xFFFFFFFF - state[8]; + // NOLINTNEXTLINE(*-implicit-bool-conversion) + const SIMD_8x32 CTR1 = SIMD_8x32(0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7); + + SIMD_8x32 R00 = SIMD_8x32::splat(state[0]); + SIMD_8x32 R01 = SIMD_8x32::splat(state[1]); + SIMD_8x32 R02 = SIMD_8x32::splat(state[2]); + SIMD_8x32 R03 = SIMD_8x32::splat(state[3]); + SIMD_8x32 R04 = SIMD_8x32::splat(state[4]); + SIMD_8x32 R05 = SIMD_8x32::splat(state[5]); + SIMD_8x32 R06 = SIMD_8x32::splat(state[6]); + SIMD_8x32 R07 = SIMD_8x32::splat(state[7]); + SIMD_8x32 R08 = SIMD_8x32::splat(state[8]) + CTR0; + SIMD_8x32 R09 = SIMD_8x32::splat(state[9]) + CTR1; + SIMD_8x32 R10 = SIMD_8x32::splat(state[10]); + SIMD_8x32 R11 = SIMD_8x32::splat(state[11]); + SIMD_8x32 R12 = SIMD_8x32::splat(state[12]); + SIMD_8x32 R13 = SIMD_8x32::splat(state[13]); + SIMD_8x32 R14 = SIMD_8x32::splat(state[14]); + SIMD_8x32 R15 = SIMD_8x32::splat(state[15]); + + for(size_t r = 0; r != rounds / 2; ++r) { + R04 ^= (R00 + R12).rotl<7>(); + R09 ^= (R05 + R01).rotl<7>(); + R14 ^= (R10 + R06).rotl<7>(); + R03 ^= (R15 + R11).rotl<7>(); + + R08 ^= (R04 + R00).rotl<9>(); + R13 ^= (R09 + R05).rotl<9>(); + R02 ^= (R14 + R10).rotl<9>(); + R07 ^= (R03 + R15).rotl<9>(); + + R12 ^= (R08 + R04).rotl<13>(); + R01 ^= (R13 + R09).rotl<13>(); + R06 ^= (R02 + R14).rotl<13>(); + R11 ^= (R07 + R03).rotl<13>(); + + R00 ^= (R12 + R08).rotl<18>(); + R05 ^= (R01 + R13).rotl<18>(); + R10 ^= (R06 + R02).rotl<18>(); + R15 ^= (R11 + R07).rotl<18>(); + + R01 ^= (R00 + R03).rotl<7>(); + R06 ^= (R05 + R04).rotl<7>(); + R11 ^= (R10 + R09).rotl<7>(); + R12 ^= (R15 + R14).rotl<7>(); + + R02 ^= (R01 + R00).rotl<9>(); + R07 ^= (R06 + R05).rotl<9>(); + R08 ^= (R11 + R10).rotl<9>(); + R13 ^= (R12 + R15).rotl<9>(); + + R03 ^= (R02 + R01).rotl<13>(); + R04 ^= (R07 + R06).rotl<13>(); + R09 ^= (R08 + R11).rotl<13>(); + R14 ^= (R13 + R12).rotl<13>(); + + R00 ^= (R03 + R02).rotl<18>(); + R05 ^= (R04 + R07).rotl<18>(); + R10 ^= (R09 + R08).rotl<18>(); + R15 ^= (R14 + R13).rotl<18>(); + } + + R00 += SIMD_8x32::splat(state[0]); + R01 += SIMD_8x32::splat(state[1]); + R02 += SIMD_8x32::splat(state[2]); + R03 += SIMD_8x32::splat(state[3]); + R04 += SIMD_8x32::splat(state[4]); + R05 += SIMD_8x32::splat(state[5]); + R06 += SIMD_8x32::splat(state[6]); + R07 += SIMD_8x32::splat(state[7]); + R08 += SIMD_8x32::splat(state[8]) + CTR0; + R09 += SIMD_8x32::splat(state[9]) + CTR1; + R10 += SIMD_8x32::splat(state[10]); + R11 += SIMD_8x32::splat(state[11]); + R12 += SIMD_8x32::splat(state[12]); + R13 += SIMD_8x32::splat(state[13]); + R14 += SIMD_8x32::splat(state[14]); + R15 += SIMD_8x32::splat(state[15]); + + SIMD_8x32::transpose(R00, R01, R02, R03, R04, R05, R06, R07); + SIMD_8x32::transpose(R08, R09, R10, R11, R12, R13, R14, R15); + + R00.store_le(output); + R08.store_le(output + 32 * 1); + R01.store_le(output + 32 * 2); + R09.store_le(output + 32 * 3); + R02.store_le(output + 32 * 4); + R10.store_le(output + 32 * 5); + R03.store_le(output + 32 * 6); + R11.store_le(output + 32 * 7); + R04.store_le(output + 32 * 8); + R12.store_le(output + 32 * 9); + R05.store_le(output + 32 * 10); + R13.store_le(output + 32 * 11); + R06.store_le(output + 32 * 12); + R14.store_le(output + 32 * 13); + R07.store_le(output + 32 * 14); + R15.store_le(output + 32 * 15); + + SIMD_8x32::zero_registers(); + + state[8] += 8; + if(state[8] < 8) { + state[9]++; + } +} +} // namespace Botan diff --git a/src/lib/stream/salsa20/salsa20_avx512/info.txt b/src/lib/stream/salsa20/salsa20_avx512/info.txt new file mode 100644 index 00000000000..6480a91d1a4 --- /dev/null +++ b/src/lib/stream/salsa20/salsa20_avx512/info.txt @@ -0,0 +1,17 @@ + +SALSA20_AVX512 -> 20260726 + + + +name -> "Salsa20 AVX512" +brief -> "Salsa20 using AVX512 instructions" + + + +avx512 + + + +simd_avx512 +cpuid + diff --git a/src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp b/src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp new file mode 100644 index 00000000000..8481bdebf00 --- /dev/null +++ b/src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp @@ -0,0 +1,131 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +//static +void BOTAN_FN_ISA_AVX512 Salsa20::salsa20_avx512_x16(uint8_t output[64 * 16], uint32_t state[16], size_t rounds) { + BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); + const SIMD_16x32 CTR0 = SIMD_16x32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + + const uint32_t C = 0xFFFFFFFF - state[8]; + + // clang-format off + const SIMD_16x32 CTR1 = SIMD_16x32( + // NOLINTNEXTLINE(*-implicit-bool-conversion) + 0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7, C < 8, C < 9, C < 10, C < 11, C < 12, C < 13, C < 14, C < 15); + // clang-format on + + SIMD_16x32 R00 = SIMD_16x32::splat(state[0]); + SIMD_16x32 R01 = SIMD_16x32::splat(state[1]); + SIMD_16x32 R02 = SIMD_16x32::splat(state[2]); + SIMD_16x32 R03 = SIMD_16x32::splat(state[3]); + SIMD_16x32 R04 = SIMD_16x32::splat(state[4]); + SIMD_16x32 R05 = SIMD_16x32::splat(state[5]); + SIMD_16x32 R06 = SIMD_16x32::splat(state[6]); + SIMD_16x32 R07 = SIMD_16x32::splat(state[7]); + SIMD_16x32 R08 = SIMD_16x32::splat(state[8]) + CTR0; + SIMD_16x32 R09 = SIMD_16x32::splat(state[9]) + CTR1; + SIMD_16x32 R10 = SIMD_16x32::splat(state[10]); + SIMD_16x32 R11 = SIMD_16x32::splat(state[11]); + SIMD_16x32 R12 = SIMD_16x32::splat(state[12]); + SIMD_16x32 R13 = SIMD_16x32::splat(state[13]); + SIMD_16x32 R14 = SIMD_16x32::splat(state[14]); + SIMD_16x32 R15 = SIMD_16x32::splat(state[15]); + + for(size_t r = 0; r != rounds / 2; ++r) { + // column round + R04 ^= (R00 + R12).rotl<7>(); + R09 ^= (R05 + R01).rotl<7>(); + R14 ^= (R10 + R06).rotl<7>(); + R03 ^= (R15 + R11).rotl<7>(); + + R08 ^= (R04 + R00).rotl<9>(); + R13 ^= (R09 + R05).rotl<9>(); + R02 ^= (R14 + R10).rotl<9>(); + R07 ^= (R03 + R15).rotl<9>(); + + R12 ^= (R08 + R04).rotl<13>(); + R01 ^= (R13 + R09).rotl<13>(); + R06 ^= (R02 + R14).rotl<13>(); + R11 ^= (R07 + R03).rotl<13>(); + + R00 ^= (R12 + R08).rotl<18>(); + R05 ^= (R01 + R13).rotl<18>(); + R10 ^= (R06 + R02).rotl<18>(); + R15 ^= (R11 + R07).rotl<18>(); + + // row round + R01 ^= (R00 + R03).rotl<7>(); + R06 ^= (R05 + R04).rotl<7>(); + R11 ^= (R10 + R09).rotl<7>(); + R12 ^= (R15 + R14).rotl<7>(); + + R02 ^= (R01 + R00).rotl<9>(); + R07 ^= (R06 + R05).rotl<9>(); + R08 ^= (R11 + R10).rotl<9>(); + R13 ^= (R12 + R15).rotl<9>(); + + R03 ^= (R02 + R01).rotl<13>(); + R04 ^= (R07 + R06).rotl<13>(); + R09 ^= (R08 + R11).rotl<13>(); + R14 ^= (R13 + R12).rotl<13>(); + + R00 ^= (R03 + R02).rotl<18>(); + R05 ^= (R04 + R07).rotl<18>(); + R10 ^= (R09 + R08).rotl<18>(); + R15 ^= (R14 + R13).rotl<18>(); + } + + R00 += SIMD_16x32::splat(state[0]); + R01 += SIMD_16x32::splat(state[1]); + R02 += SIMD_16x32::splat(state[2]); + R03 += SIMD_16x32::splat(state[3]); + R04 += SIMD_16x32::splat(state[4]); + R05 += SIMD_16x32::splat(state[5]); + R06 += SIMD_16x32::splat(state[6]); + R07 += SIMD_16x32::splat(state[7]); + R08 += SIMD_16x32::splat(state[8]) + CTR0; + R09 += SIMD_16x32::splat(state[9]) + CTR1; + R10 += SIMD_16x32::splat(state[10]); + R11 += SIMD_16x32::splat(state[11]); + R12 += SIMD_16x32::splat(state[12]); + R13 += SIMD_16x32::splat(state[13]); + R14 += SIMD_16x32::splat(state[14]); + R15 += SIMD_16x32::splat(state[15]); + + SIMD_16x32::transpose(R00, R01, R02, R03, R04, R05, R06, R07, R08, R09, R10, R11, R12, R13, R14, R15); + + R00.store_le(output); + R01.store_le(output + 64 * 1); + R02.store_le(output + 64 * 2); + R03.store_le(output + 64 * 3); + R04.store_le(output + 64 * 4); + R05.store_le(output + 64 * 5); + R06.store_le(output + 64 * 6); + R07.store_le(output + 64 * 7); + R08.store_le(output + 64 * 8); + R09.store_le(output + 64 * 9); + R10.store_le(output + 64 * 10); + R11.store_le(output + 64 * 11); + R12.store_le(output + 64 * 12); + R13.store_le(output + 64 * 13); + R14.store_le(output + 64 * 14); + R15.store_le(output + 64 * 15); + + SIMD_16x32::zero_registers(); + + state[8] += 16; + if(state[8] < 16) { + state[9]++; + } +} +} // namespace Botan diff --git a/src/lib/stream/salsa20/salsa20_simd32/info.txt b/src/lib/stream/salsa20/salsa20_simd32/info.txt new file mode 100644 index 00000000000..89f280aa9a1 --- /dev/null +++ b/src/lib/stream/salsa20/salsa20_simd32/info.txt @@ -0,0 +1,25 @@ + +SALSA20_SIMD32 -> 20260726 + + + +name -> "Salsa20 SIMD" +brief -> "Salsa20 using SIMD instructions" + + + +simd_4x32 +cpuid + + + +x86_32:ssse3 +x86_64:ssse3 +x32:ssse3 +arm32:neon +arm64:neon +ppc32:altivec +ppc64:altivec +loongarch64:lsx +wasm:simd128 + diff --git a/src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp b/src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp new file mode 100644 index 00000000000..d24ecb5da79 --- /dev/null +++ b/src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp @@ -0,0 +1,128 @@ +/* +* (C) 2026 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include + +namespace Botan { + +//static +void BOTAN_FN_ISA_SIMD_4X32 Salsa20::salsa20_simd32_x4(uint8_t output[64 * 4], uint32_t state[16], size_t rounds) { + BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); + const SIMD_4x32 CTR0 = SIMD_4x32(0, 1, 2, 3); + + const uint32_t C = 0xFFFFFFFF - state[8]; + + // NOLINTNEXTLINE(*-implicit-bool-conversion) + const SIMD_4x32 CTR1 = SIMD_4x32(0, C < 1, C < 2, C < 3); + + SIMD_4x32 R00 = SIMD_4x32::splat(state[0]); + SIMD_4x32 R01 = SIMD_4x32::splat(state[1]); + SIMD_4x32 R02 = SIMD_4x32::splat(state[2]); + SIMD_4x32 R03 = SIMD_4x32::splat(state[3]); + SIMD_4x32 R04 = SIMD_4x32::splat(state[4]); + SIMD_4x32 R05 = SIMD_4x32::splat(state[5]); + SIMD_4x32 R06 = SIMD_4x32::splat(state[6]); + SIMD_4x32 R07 = SIMD_4x32::splat(state[7]); + SIMD_4x32 R08 = SIMD_4x32::splat(state[8]) + CTR0; + SIMD_4x32 R09 = SIMD_4x32::splat(state[9]) + CTR1; + SIMD_4x32 R10 = SIMD_4x32::splat(state[10]); + SIMD_4x32 R11 = SIMD_4x32::splat(state[11]); + SIMD_4x32 R12 = SIMD_4x32::splat(state[12]); + SIMD_4x32 R13 = SIMD_4x32::splat(state[13]); + SIMD_4x32 R14 = SIMD_4x32::splat(state[14]); + SIMD_4x32 R15 = SIMD_4x32::splat(state[15]); + + for(size_t r = 0; r != rounds / 2; ++r) { + R04 ^= (R00 + R12).rotl<7>(); + R09 ^= (R05 + R01).rotl<7>(); + R14 ^= (R10 + R06).rotl<7>(); + R03 ^= (R15 + R11).rotl<7>(); + + R08 ^= (R04 + R00).rotl<9>(); + R13 ^= (R09 + R05).rotl<9>(); + R02 ^= (R14 + R10).rotl<9>(); + R07 ^= (R03 + R15).rotl<9>(); + + R12 ^= (R08 + R04).rotl<13>(); + R01 ^= (R13 + R09).rotl<13>(); + R06 ^= (R02 + R14).rotl<13>(); + R11 ^= (R07 + R03).rotl<13>(); + + R00 ^= (R12 + R08).rotl<18>(); + R05 ^= (R01 + R13).rotl<18>(); + R10 ^= (R06 + R02).rotl<18>(); + R15 ^= (R11 + R07).rotl<18>(); + + R01 ^= (R00 + R03).rotl<7>(); + R06 ^= (R05 + R04).rotl<7>(); + R11 ^= (R10 + R09).rotl<7>(); + R12 ^= (R15 + R14).rotl<7>(); + + R02 ^= (R01 + R00).rotl<9>(); + R07 ^= (R06 + R05).rotl<9>(); + R08 ^= (R11 + R10).rotl<9>(); + R13 ^= (R12 + R15).rotl<9>(); + + R03 ^= (R02 + R01).rotl<13>(); + R04 ^= (R07 + R06).rotl<13>(); + R09 ^= (R08 + R11).rotl<13>(); + R14 ^= (R13 + R12).rotl<13>(); + + R00 ^= (R03 + R02).rotl<18>(); + R05 ^= (R04 + R07).rotl<18>(); + R10 ^= (R09 + R08).rotl<18>(); + R15 ^= (R14 + R13).rotl<18>(); + } + + R00 += SIMD_4x32::splat(state[0]); + R01 += SIMD_4x32::splat(state[1]); + R02 += SIMD_4x32::splat(state[2]); + R03 += SIMD_4x32::splat(state[3]); + R04 += SIMD_4x32::splat(state[4]); + R05 += SIMD_4x32::splat(state[5]); + R06 += SIMD_4x32::splat(state[6]); + R07 += SIMD_4x32::splat(state[7]); + R08 += SIMD_4x32::splat(state[8]) + CTR0; + R09 += SIMD_4x32::splat(state[9]) + CTR1; + R10 += SIMD_4x32::splat(state[10]); + R11 += SIMD_4x32::splat(state[11]); + R12 += SIMD_4x32::splat(state[12]); + R13 += SIMD_4x32::splat(state[13]); + R14 += SIMD_4x32::splat(state[14]); + R15 += SIMD_4x32::splat(state[15]); + + SIMD_4x32::transpose(R00, R01, R02, R03); + SIMD_4x32::transpose(R04, R05, R06, R07); + SIMD_4x32::transpose(R08, R09, R10, R11); + SIMD_4x32::transpose(R12, R13, R14, R15); + + R00.store_le(output + 0 * 16); + R04.store_le(output + 1 * 16); + R08.store_le(output + 2 * 16); + R12.store_le(output + 3 * 16); + R01.store_le(output + 4 * 16); + R05.store_le(output + 5 * 16); + R09.store_le(output + 6 * 16); + R13.store_le(output + 7 * 16); + R02.store_le(output + 8 * 16); + R06.store_le(output + 9 * 16); + R10.store_le(output + 10 * 16); + R14.store_le(output + 11 * 16); + R03.store_le(output + 12 * 16); + R07.store_le(output + 13 * 16); + R11.store_le(output + 14 * 16); + R15.store_le(output + 15 * 16); + + state[8] += 4; + if(state[8] < 4) { + state[9]++; + } +} + +} // namespace Botan diff --git a/src/tests/data/stream/chacha.vec b/src/tests/data/stream/chacha.vec index c38a27c4978..61af6fc3278 100644 --- a/src/tests/data/stream/chacha.vec +++ b/src/tests/data/stream/chacha.vec @@ -1,5 +1,5 @@ -#test cpuid avx512 avx2 sse2 neon altivec lsx simd128 +#test cpuid avx512 avx2 ssse3 neon altivec lsx simd128 [ChaCha(8)] diff --git a/src/tests/data/stream/salsa20.vec b/src/tests/data/stream/salsa20.vec index 5523ebd2085..99b8ee7a948 100644 --- a/src/tests/data/stream/salsa20.vec +++ b/src/tests/data/stream/salsa20.vec @@ -1,3 +1,5 @@ +#test cpuid avx512 avx2 ssse3 neon altivec lsx simd128 + [Salsa20] Key = 000102030405060708090A0B0C0D0E0F Out = 2DD5C3F7BA2B20F76802410C688688895AD8C1BD4EA6C9B140FB9B90E21049BF583F527970EBC1 @@ -53,3 +55,29 @@ Key = 0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C Nonce = 288FF65DC42B92F9 Seek = 65479 Out = DFEC1796E921E9D6E24ECF0209BCBEA4F98370FCE629056F64917283436E2D3F45556225307D5CC5A565325D8993B37F1654195C240BF75B16ABF39A210EEE89598B7133377056C2FE + +# Long random inputs/outputs + +Key = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +Nonce = A0A1A2A3A4A5A6A7 +In = 110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E49443F3A35302B26211C17120D0803FEF9F4EFEAE5E0DBD6D1CCC7C2BDB8B3AEA9A49F9A95908B86817C77726D68635E59544F4A45403B36312C27221D18130E0904FFFAF5F0EBE6E1DCD7D2CDC8C3BEB9B4AFAAA5A09B96918C87827D78736E69645F5A55504B46413C37322D28231E19140F0A0500FBF6F1ECE7E2DDD8D3CEC9C4BFBAB5B0ABA6A19C97928D88837E79746F6A65605B56514C47423D38332E29241F1A15100B0601FCF7F2EDE8E3DED9D4CFCAC5C0BBB6B1ACA7A29D98938E89847F7A75706B66615C57524D48433E39342F2A25201B16110C0702FDF8F3EEE9E4DFDAD5D0CBC6C1BCB7B2ADA8A39E99948F8A85807B76716C67625D58534E +Out = 04CD66FC0E74099D7F93D5CB0A2B903706839FA6B219C4CFEDE860687F9F0D9BA0D883E3A5586937425F4D386457A3341626C32108AC114EBDB0099FBDE4B68C8F558ED4E6400D3F90E5E0AE20D7324E38FFFB4B3727611231BB5FE108EADCC4B99540BFA210D96305162ABF9F3519879F4DC3C4256FE7F1FF155C2CB93381D9356E8B070FBDE13BD8047721200F3BA12C8861E764C756159012226409F58E7E72A073CAE34889613874FE39537816CCD1E2D0A6C2EACA8F35BF525861C8C5426E27CBF1956B709E713E5051175C6B12E43DB97FF5293697F4B37745D7A0B29903CA67C10603AD60B1CC6DA6E095FCED1E09E1A07F7BDE22252DD4C947670219971A1B240793CFEC2B2E20AE0826B60B2B7606E45D22EC185452D1500E3209412B79B99BA1392BAACF6F7A63D0F857ACFBB820EFD9340CFF4D4AB0F5D63867E08AC3893010C755CF55AEC2FF4F913FFBDCD572A6457A0681250DD8171C097F67AFC16D6579108BD58D227FF4F851A4AB25E26251186D2372A4F254B77848DAD72832BFD6DA896A0B0F9D3E8D0FD8FEF0EBA92428A04C190CA908382B4BFF5642DACF70C8E10560C16A6A74CADC8996581363BFDBE299A8BEE7DEC499CCEA2AE11C3877A7BD08DD323148A2C1EA7396B32795EF8E662721B9E88780D85463863520BAEC1212892F2A6DA88E1CBE2D505A11E815A496BB3BA420704B685DC10B5062CF6BCA2C6E07EA833FB44CA4FB46DEFFF9A4B6A932B78C646173DD4342294ACA68BB94429009E8FF37F0C7A283C5D9B1CC1B20529DFD5263A6644E33F9478ED0BCCFB50DACE9356E3BD23603B7D201C271C4A6FE19152073AB32852B122A9C17FFFD310025713F37A36CFE352CEF6603D50EB13AE64C57ACB698C64FD143D0D221ABD0E53EC10E5CDBBBFE63B58C3D591C7EA78359F8DBF2114DE24D10DC32C4152C6E69B12F4FA644F166A17FC8A51B048ABE763979CEBB07D1B6CA91F80D868A8F563B3EFBF01356E45C99BE28663D0009E31DD1330BDE3C6F1072E627B5E9CCE0F41C37CF83B32D4E1F19CDCA8EB93E83868AA81B7060A221924A8E1DF9EF7A25D47C1F68D2358842BA76E37EC03317DEE1EAACEADB099934B34C08B7941721D952F277A30DB7C9BC095A0C2CBAD018E49ED83F9251205443ACA3C8C6A9FB0DA6EFBF9D45781C852F0AAEB4D2073B648126D8D595CE2CC4E87B68F3232026AC392D2DBDE6ED7A7FC441CAE354006A81A833331AA0FBE8D8AF094DD1A13CA2F7E2B03F2F2F2C73C7CEF55507B03332FDB99666DBADF4329162A94F7745423C954816DD4E40843205EA52D6DA4407B9E5B3268DD792D269F232C6A87EAA7F091EE995DA14A909F3025D8D0DBE521BA3CAB7777DE594ED5C7B25313647045A2B4A9078D5B7AE317C5FCEC041399F1C735258E9C12BB56C9AA7C76F54282D531DE3BC48E67956EC89DC7EB77B53EF55612311B1C5255705462ED863E2F7100E7249B2097D82EAB3EA01F8530D0938047AFA092BC6A6B4AB5AE93D337FA376CE5FC98568B0D4FC8152160C611E9357E36E28E16984F498AA227CA9806BA4CE96A6FBAB2722778990AE16C3F6C1D70BA4B76EB3A2160E79F803379D16F87B88E49F392701286E357802A6A7A69DF19B821EC1533FFA2B59E1FD7D65677FA8FDA6B9504784BAE13A5A290572C14D504286FC74F43F0C10C943C83892ED2DB2B25E80BFF4EEA237AFEA82FDF048795D975B668815794A3E230D5C82BEE31468FC51473E5F0046474D4F28D96CBC897EC8850E196C96082019A6B953FCEF42B170249A40102F536111A546A8BE5593C64E6116BDA6948BA337F1627D57BBD72E1D18CBAE2C124EC50E68BE4E49F9901B96285FB221F9E15B1FC07A85F9064C0007C8B97B7EB99DD54BF1A716F4F064F4284FD6736CB4290CFA586EDBCA30D582611B1EA61199B0528FDA4D5F7B99E3BA78638DAAFB861C108B375071C039A5D30225AE09FEDB919A1D11614A61B376E32FD2FF2D7B33839424CA80539E838BF8B7023F2FF90D79C79E700FDFA39B8471989EB4FF431A1D583C40ED849927FAB8F637183EF41DB122C64C976F13417514B3C82B5BD14488E49AE607C2881D2861A78C4DACB675CF5009E19AF8C85C2B1A708EC53383656A017FB05428CB61AE8F61A3BD5E09BDED1709426DC6CB896288320F65C650D2D7E968BD2D66777D172BA84E1CCF66BBD10E98E7716D6813D913E9297A64C9392D8ED7B8CF030501C46ED4B79C296985963BE623C2B27DEAC57CB2B3ABA95CF936B3CB4A3ACEFDA7C454879C53EE47BEE6EB2928130B87BA07E60B4E35EB891941ACD76858346120D2D800022E90A9B98ED261C3D00B6A79F38C98B78FFCF4835AB353B92C38910B740DFECA93E5311C93303A5DA25129A1CB94B8D9FF7775DBB2E6CAB87E11D7136172B290DFFD15EED4648AB93589EF283981CEFAEABFCEF8CF309C8EC1F5718B91F5841519F8CBA815F19F6A6CC69AD242C726853E0E689A5C5541EFC02BE2E3A79CDF8D59D6331D7F971376BFF9E08F473CEB01A75A922182EB12F68D96A7682720D53C1D5B519504602A9E17720D65E5571C47F18A09F21005880BCAF7727F91FB7CB4FD2D1C46D3739BDDC5E53571B5E0B62C8701BBBBCCCC704BBCA530F807AA81D75A3D5A9EED6280F6F14F455ED3C8B9021905668E3876C34934DEE624F67008DD796E055580B7E0354530619648CB3A72DA5C4CA5D18803E34D503BB6C73CA8CC2478850A41B7D543458D3890AF86EB22D96109E0F65F1CD66397A90EE2089F2A02C64D83E90B7720B5FF4BA2FDFFA7BEFEAF714CDA535A53F1F18818CC5FD4036B67E2F37FBDB525A4447AC857744A82952862AA60BC320513787496E14049EDBF7524E6144EAC5ED4943C93FE08C2A574518BD175E623775A8874AF8930ED7D7F77D338C3AD11431930FA44EE55AF9C3C64CCBCC65807B6F4B831FC48F16B441C971F57F85F93FA0A51A527467FAEAF0E525B145650E77AFA694D4FC9EFF20306DFA92764D148F736C09E7B6E7829B2A45FE268248BEC533BCC9590BE0FA3158B5EDC8A427CCC7A60D8DD1BC4402701D12592F2DC2FDFD65EFE0642202E5345FFD9DA548F97EFB30917EA6BA117E2BDF7E6469ED2FD54F26844AFD6C524B48C207FA8316167E501CFF47F93CBCA406A45042E06938FF3F5511035D9065141365CFD244825126FF7E7A49DE07FFCF1407C82B3D39488DD94CDBFA4C09A565CB0E846806D2CE165BCC83483DD655B38A47C23BE268C107D1B3C7A903231A8A034843BC35EAD0707826705AC7FB1715A3BC14DFF616D6F8481F6B50971BE097622435A120512B6E1EECC15FCC5A4BB024D0310D7B127B17813F381B5D240E945D1D3AB7CD8EE0D59BD0C520311F050DBBA9E0696394D428B92F775D8FFF43F4D9454407E2AED5989887EADE2324A8B1EAD80AD5079E87F284C1E41F43A78987461434F19FA6DAEA49DEC98CB41C94FBBE52D04F414AD355832E15206BF4DF921FCD4BB33BFCEE1155FA391A0B8C211064C64BF5302E292437DBC010F7DF24A6257EB11CD99AA0B7896C5D771F9BE919EDA3395E842E0493C8660F0DD02362836118BFB7383C18665FA7DC9DC113C949AF36601DBD483BD19ADBB01CABC67F35FB82AB205C079A06AA5 + +Key = B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF +Nonce = C0C1C2C3C4C5C6C7 +Out = 9970DB641010268E34293D566842E95F9E23091B3E4380F6BD6E07D6724394FBD6E908C248FA4D013154538544C1E493D202C8138CF172C8AF75B90BF0EE70B0BF9823E4ECF0770B117F3A8E74A3E1A873D4B69EC2E2B51737E24D7942532F500C9F862C427F23B5F8D312E118513C0FF6CEFE6EA5C3A4A09240D99F8559F59356884D1EFBFE5E4BB6555FB2808E71138BAB396DBDF8855BFFBA7B5FE702BF7F8CE730B1D5B090D82EC86F6663A4A39C0ECD033AFF7CA229AD9EDC16487E4FD3A394EE6E1E8B55612D3C2BB47CC703D47E429B227E984120042BB4D3A01199E00E6B248DDB8DEDB3F5C1F0DAB9040967110FB9EFED1520D7C3B6DABD776162F0BCA556975F8AD07E3E6D2D50A934172352A598823A0282B6A834A13A9641011B60799CF2D670A17E37970279D7CA6FCBE292F952F35D9CCF9E514DFC61F56064C4F48BE8D34E99FF46209403DDDF11F8B2F52B586AEC5468E676D9796E8DCEA3F128D7415485B91222161DD590EBE70659B73B95179BA5744DA7C802A6603A6D1C7B00811F0FD93929C6F1C9A29C1B609B41303F9BC569E679A7047A0D2FF483F613A7FE94EA451D9979C4B1181EBF1ADFEAF88ED24831C98072F6EE2CBCA5D0CEE766E3D23D814B674EBB4A3958A799232D9C6033307A11A4DD523A4BBB348EF4349F8BE09D0E03F38ACF04B82F4771402AC9BB0143F3BA4BACB415FA6A55F66DD443FF46947AD42AEA5A30AFEABD9609DDEC32AF5D9FC4D14C1FBFBA7B49E8162B90EA1F3AE328235775D7BDEDC53D3FDB3866DA920F80D6E625F167F12CE5F9E9DD364A523AC32B3C392ECC4E9FDDBF39B0EC640806DCBA6E9A9E1F7BDBB0670BD5258E608BCD7955B67D3A35AE32639DCC1197B7E1E7298CD94C7CE33BC8D6F02C59E35AD887A6E586559016AD912F522910A8C6061A47691991FC30E602C06585331CCB0AA3D155B35ED6BF013D913E6E48CB6AB15A2629AD7124373E419E74746E4712C3E82E6B24986897293DB646F5015435C574F1438007C9F093AEDCBB1D2195E6B34E0312548340ABB598ED68D87DF768A7F82B5BED2E95E51A678E6D7A7EB6AEB73D40767114AC18ADEA0EC9211D2C9DA9FA99C6928A57311EDF566424C29048F7C883C28D55224FF4B1DF61B0A5D61E1EC28B07C9573B4AA3BA880CE2723D6CF4C2F6B560EA6F559A0864E7A1B11600B76B38B0EC9007D7C838B9A519E544247331075CAF1307490C94C6AE0B8D54BE51C771C6A3FCFA73F6D1FFC9F00D7709D51059938B0F04B94130365A9E5E50FE1F0AF523EF4F2DC5E1D3AD295280418A6A9AA40B04F9FBBADA10B7EBCCEC8A99823006B60CFEFA8BE80B838A779312B005E73F7EBFC864AA9942B8076B75FEFE0EEF3B2D68772B703B3BF644A5DAB484F47B5807FAFD8CA23DF5B2C85171FF3F44A4492A60AB529D3D70BEF88FC204985B138C1356E7D4167DBF5075B28900BCF7AB548A98C7592E1E600C0F48D677E462E48C40A2485C772A3EAFE03492A8E2D9BC8C86BEE76188025D0ADCAE665408A34EAD0F37FE79098726A61E902C21BD67815E870C61F77A8EE48E6901B89573DFE12FA4722748DF128D813F0F5B811AF81AF601A8D431228BDCD37D6783A8FDE9B22D182B84CEAC0E5F3C4233EFE8E2006225F07E9517E45046E65077A3708DC33F4267D2636ADA4B2B89312A255BE2B64565A3BF51A90ABC10EE9E02FA26E8A11D7E1554A7DAE2B479561B24FCD64FFECB8BF561117FA0D0D497F54E61FD1B0D9353A607866C1827A610932B06B7498B44BCEA18396BF0E70DC863E0ED3070FD708A5C2A33D0B71FB8B69F53EA + +Key = 0104070A0D101316191C1F2225282B2E3134373A3D404346494C4F5255585B5E +Nonce = 02070C11161B20252A2F34393E43484D52575C61666B7075 +In = 591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0814203C4854607C8894A0BCC8D4E0FD0915213D4955617D8995A1BDC9D5E1FE0A16223E4A56627E8A96A2BECAD6E2FF0B17233F4B57637F8B97A3BFCBD7E3F00C1824304C5864708C98A4B0CCD8E4F10D1925314D5965718D99A5B1CDD9E5F20E1A26324E5A66728E9AA6B2CEDAE6F30F1B27334F5B67738F9BA7B3CFDBE7F4001C2834405C6874809CA8B4C0DCE8F5011D2935415D6975819DA9B5C1DDE9F6021E2A36425E6A76829EAAB6C2DEEAF7031F2B37435F6B77839FABB7C3DFEBF804102C3844506C788490ACB8C4D0ECF905112D3945516D798591ADB9C5D1EDFA06122E3A46526E7A8692AEBAC6D2EEFB07132F3B47536F7B8793AFBBC7D3EFFC0 +Out = 1548394700A8FA7E8AAD9C5BFD312339080FC596FAC8FB05E4F414169EEDB64AFD4A77213091FC09B541B1E311F6060CC841DDEABEBCD4BC42905599B514AB87C8382D7DF9DE8C98E727E5B9CDEB3A950A9E47480CD523A9F2B9287957EA8272842BB481918D3A01212A14BAB38FC03DE0018C105CDE7B74E72642A1E3C3FB5E4D0047CAE770F0A47B72A9D1CFB34160619D4046DA554194B0B3E5E5C06EE59052DF138337107C3D0F309E205FC21481E12F238FE70755E22CAE1D15A5ECE8A8A48011691E84B6F40A12249E3EDF76A913B411E7C8ADA8ED53204B2F4E5FD0D74E0A1107DFE03418CCFA74C54016ECF8739BA5F017E201D169246180D62785D8601ED81423F7CE389B4982333F02E8F4FE7D9D6F71B207D0720DDD7E7AC4BB61327C37546211E2DFDBB5D59DA4D5D87365A6FF177C990904D573654CC67E531E73961FE454357DF21FF7ACCCAE6D4DDB933672FFF48D64791B594C6B858CB34D8DA173084716F7740A922ADB7AB892F01A886B0816CCB855CF33DED63CCECAB996288CD53EA5EEFA0F2E8B6B81D866EF1A7DEB6E2042AC2B62193ECF4D8B040D651F9BDD3B8A31EF6730884DBC724D5748CDE0DF1C768EB8BCA3DB28491B713B50418F6DC491B852964EEFCBCB2A1F9AA8A49114F1DBC23BC0D890C139DE08B0D9176ADB97CBAE0A928B8062947FF449632B0B081BFE470DC5531504B02105548F97F417DB8D5883916EB2C944EE0A348254623B18EFD760E1A2D7645B926777C5D072940666B798006D1BA5710B7FA167F4593993F6BD8D9041CCEECBD8207B5F653CF495AA2763D5923A04B9F99C6B78D61ECB678441C419A7865CE6F6FC09FCA72A9EC583C516187B7609C6DF39AA38715EBF6F41084D0B94AB441779C681DCD62D016A8B1596235B10B7127B59338C0615BD68DF1F5CC6AE5FF2726E06156C95C4555E6A8BF3E34376F23D43842578543C6C94E4A4572A51CAC4CD00D829B8E318436CFEBE5C04092CBA3EA58F168ABD4A72A3053E5FE7AF70763847978E01B3027781E9AF7B2A04FB22DF280D6BF41B970043BEF6CE4FFBE80C0EDD69A06437804D9C36D616FE6D5981CB0AFFE844D5E8B802466DDC6377AB4CDB87607A9A87157B6F489E2B6D93B7AF31E35905F82EDEC6862A3F0130EECFD7E1DEAA9FAF2BF9937DFA43EACC3439D6EB7180F587035C3937B8B7E6ACA3F7EA2988106358ACC462A985BB8A66F5E54C073137E5808420F6FA365EC42783D5EE265F96D7E69C1F482990D0FB93D1DBD8F9819D193B147263566B841001276D3C42885F57A4A3B1A498AF872133EFD5A35EF8F2E02E6C7D6B96360FBBD2E40DFD7FD6CDB50A5B277E723960B32B537691A7E3991A171A4A984D133A509AAD569FD7C3DB613C52DDC26F6B9871F7880CA1BB06FCFBEF6E895CDB465B48C52C59126CF18BFECCFC3BB10CA3A9274F3F60BA5035473763D002697924FEDBF530A013379CB37FE6DCB1E730161EA862D682D795ED126BFFC3377A3183CB4496B47B3A2A3ED47A9E5B38B0A5375D182B3834777A420848E76EF686C7A331B281C0EEA471F138E54D332F8706EF253123AF422EF9F7D5BED6CCB558AAE158FB553CA15CBE216730A49D443DA11FBFB73232398960D81367935BE9934B20533AD600E0E9FA0A5E6B75AD7E7E28D681C0E3A41F73F1BA02778D6697404F6010D137BF63923E3C64319E3C682B36516524CD2EEC6846F7EEDFABD660B159D770D81F87BF7503325F106759B504C129817D4CF13708CC2EAD61395C4BF3B9DCD9D3DB85C7769A3128CFED978AC9630572384E959C16C2D556FF9D168B497C2FCB5194F840671CF4346AAE69B012F218D43F11E889059562509E3E53E34969A8905FB9CB1963ABCBF3E99A86FB030AE9DE75A87D9C716DE2391F001CBF4F66054D0F83BD0B8E1552DC2B0CCA3DEA2785F921A65453C428A324F0BE24C5FBA19484F1AE7FCB1D5D243E580688C69D83BCD908373FB0B06B32C014F3509DA666079CC2574A5EE8BB9DAF5E14EC9089982B1CB191A8155AA05264C6195CBEFCAECB727120626948674F71AEF3ACFEE72F3D9F0F9D72A4460EDFF9BDDB23E76B45B1F0EEB063289FCBAD82E282698111656180E3DD7A59AC943FDC4BEBE38D7ADA13B34D8BAF51549931A74112A21F6EF91E3F58158E79A22F39755D778EC522D059D1BFA0B3B64EA7F420FFC5E7C4EC7811014BE3EC7BF8B15DDC78E190C304064D39A63FB927DBE1F9FBDBD01570321B1719B0CA1F42E0B8B2B2A25D20D7A4E8F2D7034433CC8DE5B3B1C68BD8F3DAAA33FC5BE057B2763A42BAE5F1DCBB0411A1A37FC24EE06E315AF16417FB00056A7CB9EC4A264873E32F1AC4BD9171307B823A7A0AE119203FE673C63EA49B1B7AE4B9ED55EDAB64C3C9FC695324CFADE1AD123E351A918BFC086204A50E80234E4F05B871051C5A644C052D22514728A114D4772A6894ACB419100E25AE4EACD1905C493850FBD1169C69B355F1E9BC89EA102F5717D447F64C5D9B28ADE1EE21D20B370553BA1F78DE87A3A24C8BBCB43311830A9D405971A254F43F11199F54B6EC5D4684B72CBAC610B7BD9EDDF532D618E31E8821AAB19E92F76A9A2F8654745B426B6D64B8A799780A5DEE4E3D271B17B23118DE78EBDBCCCAED9364B4C1EE96C2F9B506D87474F026039BB54F9F603D526DB1F2D4B5C6EF5EEA8798815430A2A204CA1885A9684AA3643D612064B4D298093AC4CB36899B0F30597125C8454701EB7D62036FBDDC19B6A221F68700EC8ACE6B37F717BCED888E14AADC0A2C62AEBCFD8C236702D7EF1B472DA2D621C2B5F4DA871F7707B39425F91678D8950BA82B52B46383C75A38E04865FD55AF60F9A1B36D6A775D2089AF7B09EE75E1620ACBE352A58BF0BE7803328C110ABEC6911F959C3415025A509F2BCDEE329C2939D267F7419656A1E11F6A11D0284B3F6B1124D321A49087181DC9C68C9DD62234428E0ACDC0E2685F7373A39C94DD745DAFE7D34ECC5ADD2E11F24E51867A3F93ED82988E4C9188990C36164AB5DF953B5421FA612DB6415881044BB5F8F9D937A588FFEA2132109A6B085D16F11A589244FBE52358ECCCF5A14D0E8B62828A3A4FAAB08265D9E71227BCC6BAAFA24B3B01D0DEDF65F307E46F14547104138650989240DD50DB53F1D307764494E9380E2BE12520E811E18DCF3159D152D35C7DA390195182A69D9DEADB3DE438835A25351AA70B874BEDF19668C8246B062124318B9877A4B96B4F45A6FD5BDFEA1D5583FB1C7B6F2BAB0A95D49544BE84EE33D665A39178A846FC2FED3BD28E991E5D37464F7F54D544C5DC40088EE8CCCC9559E621A9DDF225E1AA2FF18EBEA774488662D1298CAE685E22EA1FF405F675F52017A65CE7A5E3A0DD51219DEE4720D1B64C2A86FD7717A20EB09A85C1BBA681844EB65E1517596D27D8D864C1EB64DBF5D8C68FFAE8ED09811B6D0A6711FCFCF42A78EFCBEC9188D57D29CB19685D786574AFDABCEF300C4E70228930C1C0193D33D036EE65CE5AC3B4C7E45491F7D9C76F916FBD195F599FBB5CE3361FA01015E6A83CADF9667DE1B412044C712F7CF2D0FF8BC7E5D0A9C117F9BE80AF8A6189134771AF1920D754AF96B179F2FD17FB820DA30FEC8C570BBA20592F8BD272 + +Key = FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0 +Nonce = D0D1D2D3D4D5D6D7 +Seek = 274877906816 +Out = 4F674BFD84EADD028BC246430B105B51F842A148794C71B74D56F79960FF0868166AA2F09CDE0F4F96A0EAC17E667767964053BCD1673C7D51348CE77833F437BEAD720D82DA8BB8A276E9C665D9BC7DC5083CDE47B2717F11886FF7BF4BE58FDD4D81F76F35A990EF4AF9FAC1068249205AAFB622FBFF7519BA4B274E9D6121D01E7815054484F88EFA817DF2885689D36DE63BF002197B9856EAC60730EB8DC32409C791017AE859FC8C40C3ED7F1CB8045F230378F4936D61343BE3E612E2DD4286DF6605BAE21B0C4C41614799127FA5064A1D59F9FCE4950FA6D2EF94AFC4E430147D09D485F345233C2D8C833E7B474455E571A8C497D42B0C92A89C1FA417E85AD7C53C2612920C3F5B83A03A7DDF080AEA6C07637BCDAE51A70CC23DB026E17391FC84BF1C04C31A125FEB1A536A8321DAEAB948CF02275D0D5697EF5187EA261F1F69FFD3E70E80D3BFC5C5A049678C08EF73F552DD2D28308D5959798BE9544E1D84E00A8364E79CC8AD85E5F9E3AADDFC7F919759AF810C776B8CE617F25003B06ADD011AE8E068A79ED563AC827B7D792F1A6D4C434F1573330E96C320487B4105D90D3CC12696DEB9AA0552BEBD01A4426BDA8483242B7DAFBCF4589A202386FAFDC89907EF4654DB301F82C593F5984F2B225EA8B6D90E11442455EA239D06C7857457E6356361FFDA63E567590C318A4A2EC855D73C89B11D95EB9DD5058A1B46391516300A3B9828984D205D4E519CA63BEDA63791BB1F84A48BE8DB0F91535CC0562B9850E74A37AE235DAF7AA8AB7C9280C3FE7118E703D12917D564DDD6EB95FADE113F5C7DEDAFB3A8AD44D06B2237B48EF0731CDC4770B8422869F9A871AF3F8D3D41CECFFAAA81CDF150A16AF1D53CFD54766FFF01A335FCD020F8D24299047D9143DEC85D40624F30D906F0A5A54EC706E97ECDE6D40223A32EFF9F96D8E1023438A113AB83197A9BB1275E68CA29F68DF3D33340B387A77DF8563DDC94DB8E8EA9FA74273B0288CE9FE08374CC12E54B82BABF46A962C5C7A0FCD315143B2BE70840360404852BF863174AAC893FD398A98FD51AE5F0D1C4FFC369CFE4952936DED1F250D64C0E4D0360E674E901003D5E72DFC41C24B911000CCBE109FFF8B8202CA25CF3220F45EA08F9A2AC3D67BB519B1044E8ED939CA3A90DF0CDD12284C4DBBC6D2152771356C75FD74441430C4EA2638E0549F33586F16AFBAE00EE832DEEE876927D7F2A34664AD33DE9142FCE75FB64AD6694C229C76EE497167DA18D1B74FF83C63D0E1F3F522CB025857BF936CE0E08FDE6C0589AC1A3F333B44EEBBCDA2CA8CA9A3E9EE1ACE2640EA08E3186DC0B12C131B3DA0727092EA3BED4890E08B34850DA784486CE6B1B97615E04E645F39EE962BF51C1F32462DA80D8B612C90F6059D700C9F02D36F56D66BE400BB94C676B5B24DD8078D79E074CB0284693D6C085D4E829BB2D877476B009E55002ED0F4A0CA3CFF5AA01FFFE81165B5028F0DCAFE69E965BC0C6295CDD5A6153726F149B0E199B259708C6631344598CBC67BE610465E1B6134FA3CBD6E71BE6D61E37E56D70E4EF24B48319580D9C8E1FB61E7FEA5902C9B94546DAF913F56DF636B4F550621E9412EBD3C1DF1FDBCFD537BE8200D3BD31B4A3BCD23B8B4BAB9B0855DA42F58C312A47000EAED86D39C76DCAC6AD2B712F102A0CC834C3EFAB9D203962862D1490ED5F8FFEF0E9CDF77A0B9BE473A55CE74D8DA00BF2C13835BE469FE74EDEA32EF001271E55CB5AA485629D5FD0CD500EEBE64168DA24A295B6457140B8D4E26B28B3659E41219EFCED046589B6B2D4D9EE5624D044B8B66272470D9B20302972AEBAACDC8E67324CF561403667F4F9F6B64D986A5A86842C51EEE9E71FD192439FE07F031134B36F37593D6DD502A8C13C28ACCFE6F61BAFAF7CE0CF0BC8965F33E286544FF3D827A5E0C3209F600BED020C0D249FF94D1667A08A7BB5A26AF3819E21C1DB3A158F79FF6AABE016323F37CD29B3771EDCCCC2D11CE4B4A45005EB35E61B53EE6D0482F99D6EC8CCA72C8E98DB3BC82B4F3E482880B917C471043E28F8B5AECEE299C9AAF9F610F8F65856EA9161E8AB6B52A98A2C811781E1D6C2E52921F9085089DA8B3295F5A74C142EE10B587D710D63C591BAA467C2F79FB9C27E71C7A1EC13FE161A030A697C19B1F29980B94AA827A0D99CF04F83C5AF46E549A769BBF121EB0C91DE1CB3887EBD8B83CC038E28CC1FD2C4D616B8ED336C8E2C5BC3B27CE08B8DF6EB8B29E42FE369CF2ED24E8D97C9E54B48770C6DA5E69C15EAD8A604E2091247D54CB9B32B86239139B36EF8DBDA98D5F67413D6DDC4379554B4339021E49967EFCA75B1689BC4D30C28AF3551E8EB0A964441819F70ADFC05C00F4F0C5F70A8CE97C2482B68A08733E7B4C13F82C08FF5E1F76FA8BB4740438A014540927D7145CB469570CD5713ADE1A0CB156DB2CB4750C1311EC3AFA5E4B4974FC069FDD22F4B40E0606FCEC6CAAD22ABBDD9B0CB6881BCCDBE8599666BE0A6F06ADFA970B39F0C20E67A637773619C8EBDC9119329472598DF7C6414A6C8862CFF1E799265BD74FD1FCF1A540EE112128F5348039E822D7BD15DCD25814AF88A52526C62CD59C2C4E057D44595C3AF0CFDC29BEE8D6F63459289AC8EE6851F83CA43449B2BA7423A8E314DF633C5F797D4A2D5E35F55158FEEDFE00295DC0FEC2BC76B7675B371C4879E015736AD95FFD20702069CE22690CE287CD2CA550B6B221A41098B735902F581CAA461CE85E1F35E49054550CF13C5DEF8B022A15A19FC4597EB8235FC584012FEC4E41EAA027AD6CC67998BE01BA80B4D27ABF41976800F59C9CDBA3DFF1B05821E05AD628FD88E8E + +Key = 21282F363D444B525960676E757C838A91989FA6ADB4BBC2C9D0D7DEE5ECF3FA +Nonce = E0E1E2E3E4E5E6E7 +Seek = 4000 +Out = 7E5053DF5DFD110B1C9AFE3DE49E7D11F1A44C4FA4F92753CBF3293E878F08DC81E6BAEFBC300528614D68A41498232A85A481CFDB899038EC33335F39A300E2520D1C84D5E531D99D575DAE729D962ACA82F31F6F2CDDC3ED3FFF6DEC22802108B96C795FD489873CE89A98DA782E3BCAE2EF345E708FBCCC72ABEC0B2532F567096BA5A2247BAAC6B97DD4F3F905691114886BDD6D0AC95A0D5ECD303BD79D0C9C0272C8AA4874F8AD2A5F1E5B704BB280A8D6381C3CF2E65F2F46DD596748F8248E0E271FBF3F286C9609EA01EEAB1ABD05882432CFD46E5277FEF36CC9E1F6852AC09963E9E13BC6D2C4093C53A7CBE7AC3DD319492E6CA8FDF4D68153D9B79D39D74351D81738E5B54B78A689DCB5FCF16A8354DFC64EB14C2DABBA08D5C8A6FD454F85CCB494DBF9CF02D757B9B427E7E878511CF122FD5FE8A161360CFDE6562C630461FC2D6983A8CF86843BD1D68E19FA215419A2EED026997328D0D7CD7F52108D420169467FF8919E9305E7B83744B07152409317F3C06BA9D813C9CE4B7D2BE45C63ABAF7CA3EFBC2268B81055A01DEE8C0D13E317550AE8B0B49750ADAB91D886B6FBE309AC077C8AF055C533BAE213A97528835C398BD54784A7133C0EEAF98E23C7F4793967B979652A657C6ED7AAF6A93C029FD2D07AA044D973C51F6531D385DCF30868F1890C9951C31DBA59915A9679CB019A3D0B99CE5F03E63BA8AAF860F51034C6F635162B0D80D7F0E6B5C46C06582D6C26858C259F5CF0CA06049976A77624BAE47B58D66A117533024F5416EEFCF2A41A465E7B6B69B01CE448526C80B9D5F27159E7B3BFB035273463CB41 diff --git a/src/tests/test_stream.cpp b/src/tests/test_stream.cpp index 36c9c5474dd..134667c1cfa 100644 --- a/src/tests/test_stream.cpp +++ b/src/tests/test_stream.cpp @@ -230,6 +230,25 @@ class Stream_Cipher_Tests final : public Text_Based_Test { result.test_bin_eq(provider + " write_keystream", buf, expected); } + { + // A single large request exercises any internal multi-block refill paths + cipher->set_key(key); + + cipher->set_iv(nonce.data(), nonce.size()); + + if(seek != 0) { + cipher->seek(seek); + } + + std::vector buf(input.size()); + cipher->write_keystream(buf.data(), buf.size()); + + for(size_t i = 0; i != input.size(); ++i) { + buf[i] ^= input[i]; + } + result.test_bin_eq(provider + " write_keystream one-shot", buf, expected); + } + result.test_is_true("key set", cipher->has_keying_material()); cipher->clear(); result.test_is_false("key not set", cipher->has_keying_material()); From 6bd2b9e12a4400dab1874c80a865b83a4acd8981 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 26 Jul 2026 11:21:45 -0400 Subject: [PATCH 2/2] Simplify counter setup for Salsa and ChaCha SIMD implementations --- .../stream/chacha/chacha_avx2/chacha_avx2.cpp | 15 +++++++------ .../chacha/chacha_avx512/chacha_avx512.cpp | 20 +++++++----------- .../chacha/chacha_simd32/chacha_simd32.cpp | 16 +++++++------- .../salsa20/salsa20_avx2/salsa20_avx2.cpp | 15 +++++++------ .../salsa20/salsa20_avx512/salsa20_avx512.cpp | 20 +++++++----------- .../salsa20/salsa20_simd32/salsa20_simd32.cpp | 16 +++++++------- src/lib/utils/simd/simd_4x32/simd_4x32.h | 21 +++++++++++++++++++ src/lib/utils/simd/simd_avx2/simd_avx2.h | 11 ++++++++++ src/lib/utils/simd/simd_avx512/simd_avx512.h | 9 ++++++++ 9 files changed, 85 insertions(+), 58 deletions(-) diff --git a/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp b/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp index 53bc1bd016d..ea0641e0e2d 100644 --- a/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp +++ b/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp @@ -16,11 +16,10 @@ void BOTAN_FN_ISA_AVX2 ChaCha::chacha_avx2_x8(uint8_t output[64 * 8], uint32_t s SIMD_8x32::reset_registers(); BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); - const SIMD_8x32 CTR0 = SIMD_8x32(0, 1, 2, 3, 4, 5, 6, 7); - const uint32_t C = 0xFFFFFFFF - state[12]; - // NOLINTNEXTLINE(*-implicit-bool-conversion) - const SIMD_8x32 CTR1 = SIMD_8x32(0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7); + const SIMD_8x32 CTR_LO = SIMD_8x32::splat(state[12]) + SIMD_8x32(0, 1, 2, 3, 4, 5, 6, 7); + // Carry into the high counter word for lanes whose low word wrapped + const SIMD_8x32 CTR_HI = SIMD_8x32::splat(state[13]) - CTR_LO.unsigned_lt(SIMD_8x32::splat(state[12])); SIMD_8x32 R00 = SIMD_8x32::splat(state[0]); SIMD_8x32 R01 = SIMD_8x32::splat(state[1]); @@ -34,8 +33,8 @@ void BOTAN_FN_ISA_AVX2 ChaCha::chacha_avx2_x8(uint8_t output[64 * 8], uint32_t s SIMD_8x32 R09 = SIMD_8x32::splat(state[9]); SIMD_8x32 R10 = SIMD_8x32::splat(state[10]); SIMD_8x32 R11 = SIMD_8x32::splat(state[11]); - SIMD_8x32 R12 = SIMD_8x32::splat(state[12]) + CTR0; - SIMD_8x32 R13 = SIMD_8x32::splat(state[13]) + CTR1; + SIMD_8x32 R12 = CTR_LO; + SIMD_8x32 R13 = CTR_HI; SIMD_8x32 R14 = SIMD_8x32::splat(state[14]); SIMD_8x32 R15 = SIMD_8x32::splat(state[15]); @@ -173,8 +172,8 @@ void BOTAN_FN_ISA_AVX2 ChaCha::chacha_avx2_x8(uint8_t output[64 * 8], uint32_t s R09 += SIMD_8x32::splat(state[9]); R10 += SIMD_8x32::splat(state[10]); R11 += SIMD_8x32::splat(state[11]); - R12 += SIMD_8x32::splat(state[12]) + CTR0; - R13 += SIMD_8x32::splat(state[13]) + CTR1; + R12 += CTR_LO; + R13 += CTR_HI; R14 += SIMD_8x32::splat(state[14]); R15 += SIMD_8x32::splat(state[15]); diff --git a/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp b/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp index 089e82a461c..55addbc808e 100644 --- a/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp +++ b/src/lib/stream/chacha/chacha_avx512/chacha_avx512.cpp @@ -14,15 +14,11 @@ namespace Botan { //static void BOTAN_FN_ISA_AVX512 ChaCha::chacha_avx512_x16(uint8_t output[64 * 16], uint32_t state[16], size_t rounds) { BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); - const SIMD_16x32 CTR0 = SIMD_16x32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - const uint32_t C = 0xFFFFFFFF - state[12]; - - // clang-format off - const SIMD_16x32 CTR1 = SIMD_16x32( - // NOLINTNEXTLINE(*-implicit-bool-conversion) - 0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7, C < 8, C < 9, C < 10, C < 11, C < 12, C < 13, C < 14, C < 15); - // clang-format on + const SIMD_16x32 CTR_LO = + SIMD_16x32::splat(state[12]) + SIMD_16x32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + // Carry into the high counter word for lanes whose low word wrapped + const SIMD_16x32 CTR_HI = SIMD_16x32::splat(state[13]) - CTR_LO.unsigned_lt(SIMD_16x32::splat(state[12])); SIMD_16x32 R00 = SIMD_16x32::splat(state[0]); SIMD_16x32 R01 = SIMD_16x32::splat(state[1]); @@ -36,8 +32,8 @@ void BOTAN_FN_ISA_AVX512 ChaCha::chacha_avx512_x16(uint8_t output[64 * 16], uint SIMD_16x32 R09 = SIMD_16x32::splat(state[9]); SIMD_16x32 R10 = SIMD_16x32::splat(state[10]); SIMD_16x32 R11 = SIMD_16x32::splat(state[11]); - SIMD_16x32 R12 = SIMD_16x32::splat(state[12]) + CTR0; - SIMD_16x32 R13 = SIMD_16x32::splat(state[13]) + CTR1; + SIMD_16x32 R12 = CTR_LO; + SIMD_16x32 R13 = CTR_HI; SIMD_16x32 R14 = SIMD_16x32::splat(state[14]); SIMD_16x32 R15 = SIMD_16x32::splat(state[15]); @@ -175,8 +171,8 @@ void BOTAN_FN_ISA_AVX512 ChaCha::chacha_avx512_x16(uint8_t output[64 * 16], uint R09 += SIMD_16x32::splat(state[9]); R10 += SIMD_16x32::splat(state[10]); R11 += SIMD_16x32::splat(state[11]); - R12 += SIMD_16x32::splat(state[12]) + CTR0; - R13 += SIMD_16x32::splat(state[13]) + CTR1; + R12 += CTR_LO; + R13 += CTR_HI; R14 += SIMD_16x32::splat(state[14]); R15 += SIMD_16x32::splat(state[15]); diff --git a/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp b/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp index 4bddc23d119..d6329d29371 100644 --- a/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp +++ b/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp @@ -14,12 +14,10 @@ namespace Botan { //static void BOTAN_FN_ISA_SIMD_4X32 ChaCha::chacha_simd32_x4(uint8_t output[64 * 4], uint32_t state[16], size_t rounds) { BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); - const SIMD_4x32 CTR0 = SIMD_4x32(0, 1, 2, 3); - const uint32_t C = 0xFFFFFFFF - state[12]; - - // NOLINTNEXTLINE(*-implicit-bool-conversion) - const SIMD_4x32 CTR1 = SIMD_4x32(0, C < 1, C < 2, C < 3); + const SIMD_4x32 CTR_LO = SIMD_4x32::splat(state[12]) + SIMD_4x32(0, 1, 2, 3); + // Carry into the high counter word for lanes whose low word wrapped + const SIMD_4x32 CTR_HI = SIMD_4x32::splat(state[13]) - CTR_LO.unsigned_lt(SIMD_4x32::splat(state[12])); SIMD_4x32 R00 = SIMD_4x32::splat(state[0]); SIMD_4x32 R01 = SIMD_4x32::splat(state[1]); @@ -33,8 +31,8 @@ void BOTAN_FN_ISA_SIMD_4X32 ChaCha::chacha_simd32_x4(uint8_t output[64 * 4], uin SIMD_4x32 R09 = SIMD_4x32::splat(state[9]); SIMD_4x32 R10 = SIMD_4x32::splat(state[10]); SIMD_4x32 R11 = SIMD_4x32::splat(state[11]); - SIMD_4x32 R12 = SIMD_4x32::splat(state[12]) + CTR0; - SIMD_4x32 R13 = SIMD_4x32::splat(state[13]) + CTR1; + SIMD_4x32 R12 = CTR_LO; + SIMD_4x32 R13 = CTR_HI; SIMD_4x32 R14 = SIMD_4x32::splat(state[14]); SIMD_4x32 R15 = SIMD_4x32::splat(state[15]); @@ -172,8 +170,8 @@ void BOTAN_FN_ISA_SIMD_4X32 ChaCha::chacha_simd32_x4(uint8_t output[64 * 4], uin R09 += SIMD_4x32::splat(state[9]); R10 += SIMD_4x32::splat(state[10]); R11 += SIMD_4x32::splat(state[11]); - R12 += SIMD_4x32::splat(state[12]) + CTR0; - R13 += SIMD_4x32::splat(state[13]) + CTR1; + R12 += CTR_LO; + R13 += CTR_HI; R14 += SIMD_4x32::splat(state[14]); R15 += SIMD_4x32::splat(state[15]); diff --git a/src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp b/src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp index 5335f8ca736..997bdfb5168 100644 --- a/src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp +++ b/src/lib/stream/salsa20/salsa20_avx2/salsa20_avx2.cpp @@ -16,11 +16,10 @@ void BOTAN_FN_ISA_AVX2 Salsa20::salsa20_avx2_x8(uint8_t output[64 * 8], uint32_t SIMD_8x32::reset_registers(); BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); - const SIMD_8x32 CTR0 = SIMD_8x32(0, 1, 2, 3, 4, 5, 6, 7); - const uint32_t C = 0xFFFFFFFF - state[8]; - // NOLINTNEXTLINE(*-implicit-bool-conversion) - const SIMD_8x32 CTR1 = SIMD_8x32(0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7); + const SIMD_8x32 CTR_LO = SIMD_8x32::splat(state[8]) + SIMD_8x32(0, 1, 2, 3, 4, 5, 6, 7); + // Carry into the high counter word for lanes whose low word wrapped + const SIMD_8x32 CTR_HI = SIMD_8x32::splat(state[9]) - CTR_LO.unsigned_lt(SIMD_8x32::splat(state[8])); SIMD_8x32 R00 = SIMD_8x32::splat(state[0]); SIMD_8x32 R01 = SIMD_8x32::splat(state[1]); @@ -30,8 +29,8 @@ void BOTAN_FN_ISA_AVX2 Salsa20::salsa20_avx2_x8(uint8_t output[64 * 8], uint32_t SIMD_8x32 R05 = SIMD_8x32::splat(state[5]); SIMD_8x32 R06 = SIMD_8x32::splat(state[6]); SIMD_8x32 R07 = SIMD_8x32::splat(state[7]); - SIMD_8x32 R08 = SIMD_8x32::splat(state[8]) + CTR0; - SIMD_8x32 R09 = SIMD_8x32::splat(state[9]) + CTR1; + SIMD_8x32 R08 = CTR_LO; + SIMD_8x32 R09 = CTR_HI; SIMD_8x32 R10 = SIMD_8x32::splat(state[10]); SIMD_8x32 R11 = SIMD_8x32::splat(state[11]); SIMD_8x32 R12 = SIMD_8x32::splat(state[12]); @@ -89,8 +88,8 @@ void BOTAN_FN_ISA_AVX2 Salsa20::salsa20_avx2_x8(uint8_t output[64 * 8], uint32_t R05 += SIMD_8x32::splat(state[5]); R06 += SIMD_8x32::splat(state[6]); R07 += SIMD_8x32::splat(state[7]); - R08 += SIMD_8x32::splat(state[8]) + CTR0; - R09 += SIMD_8x32::splat(state[9]) + CTR1; + R08 += CTR_LO; + R09 += CTR_HI; R10 += SIMD_8x32::splat(state[10]); R11 += SIMD_8x32::splat(state[11]); R12 += SIMD_8x32::splat(state[12]); diff --git a/src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp b/src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp index 8481bdebf00..903b9c39ec4 100644 --- a/src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp +++ b/src/lib/stream/salsa20/salsa20_avx512/salsa20_avx512.cpp @@ -14,15 +14,11 @@ namespace Botan { //static void BOTAN_FN_ISA_AVX512 Salsa20::salsa20_avx512_x16(uint8_t output[64 * 16], uint32_t state[16], size_t rounds) { BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); - const SIMD_16x32 CTR0 = SIMD_16x32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - const uint32_t C = 0xFFFFFFFF - state[8]; - - // clang-format off - const SIMD_16x32 CTR1 = SIMD_16x32( - // NOLINTNEXTLINE(*-implicit-bool-conversion) - 0, C < 1, C < 2, C < 3, C < 4, C < 5, C < 6, C < 7, C < 8, C < 9, C < 10, C < 11, C < 12, C < 13, C < 14, C < 15); - // clang-format on + const SIMD_16x32 CTR_LO = + SIMD_16x32::splat(state[8]) + SIMD_16x32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + // Carry into the high counter word for lanes whose low word wrapped + const SIMD_16x32 CTR_HI = SIMD_16x32::splat(state[9]) - CTR_LO.unsigned_lt(SIMD_16x32::splat(state[8])); SIMD_16x32 R00 = SIMD_16x32::splat(state[0]); SIMD_16x32 R01 = SIMD_16x32::splat(state[1]); @@ -32,8 +28,8 @@ void BOTAN_FN_ISA_AVX512 Salsa20::salsa20_avx512_x16(uint8_t output[64 * 16], ui SIMD_16x32 R05 = SIMD_16x32::splat(state[5]); SIMD_16x32 R06 = SIMD_16x32::splat(state[6]); SIMD_16x32 R07 = SIMD_16x32::splat(state[7]); - SIMD_16x32 R08 = SIMD_16x32::splat(state[8]) + CTR0; - SIMD_16x32 R09 = SIMD_16x32::splat(state[9]) + CTR1; + SIMD_16x32 R08 = CTR_LO; + SIMD_16x32 R09 = CTR_HI; SIMD_16x32 R10 = SIMD_16x32::splat(state[10]); SIMD_16x32 R11 = SIMD_16x32::splat(state[11]); SIMD_16x32 R12 = SIMD_16x32::splat(state[12]); @@ -93,8 +89,8 @@ void BOTAN_FN_ISA_AVX512 Salsa20::salsa20_avx512_x16(uint8_t output[64 * 16], ui R05 += SIMD_16x32::splat(state[5]); R06 += SIMD_16x32::splat(state[6]); R07 += SIMD_16x32::splat(state[7]); - R08 += SIMD_16x32::splat(state[8]) + CTR0; - R09 += SIMD_16x32::splat(state[9]) + CTR1; + R08 += CTR_LO; + R09 += CTR_HI; R10 += SIMD_16x32::splat(state[10]); R11 += SIMD_16x32::splat(state[11]); R12 += SIMD_16x32::splat(state[12]); diff --git a/src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp b/src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp index d24ecb5da79..6a3ab279027 100644 --- a/src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp +++ b/src/lib/stream/salsa20/salsa20_simd32/salsa20_simd32.cpp @@ -14,12 +14,10 @@ namespace Botan { //static void BOTAN_FN_ISA_SIMD_4X32 Salsa20::salsa20_simd32_x4(uint8_t output[64 * 4], uint32_t state[16], size_t rounds) { BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds"); - const SIMD_4x32 CTR0 = SIMD_4x32(0, 1, 2, 3); - const uint32_t C = 0xFFFFFFFF - state[8]; - - // NOLINTNEXTLINE(*-implicit-bool-conversion) - const SIMD_4x32 CTR1 = SIMD_4x32(0, C < 1, C < 2, C < 3); + const SIMD_4x32 CTR_LO = SIMD_4x32::splat(state[8]) + SIMD_4x32(0, 1, 2, 3); + // Carry into the high counter word for lanes whose low word wrapped + const SIMD_4x32 CTR_HI = SIMD_4x32::splat(state[9]) - CTR_LO.unsigned_lt(SIMD_4x32::splat(state[8])); SIMD_4x32 R00 = SIMD_4x32::splat(state[0]); SIMD_4x32 R01 = SIMD_4x32::splat(state[1]); @@ -29,8 +27,8 @@ void BOTAN_FN_ISA_SIMD_4X32 Salsa20::salsa20_simd32_x4(uint8_t output[64 * 4], u SIMD_4x32 R05 = SIMD_4x32::splat(state[5]); SIMD_4x32 R06 = SIMD_4x32::splat(state[6]); SIMD_4x32 R07 = SIMD_4x32::splat(state[7]); - SIMD_4x32 R08 = SIMD_4x32::splat(state[8]) + CTR0; - SIMD_4x32 R09 = SIMD_4x32::splat(state[9]) + CTR1; + SIMD_4x32 R08 = CTR_LO; + SIMD_4x32 R09 = CTR_HI; SIMD_4x32 R10 = SIMD_4x32::splat(state[10]); SIMD_4x32 R11 = SIMD_4x32::splat(state[11]); SIMD_4x32 R12 = SIMD_4x32::splat(state[12]); @@ -88,8 +86,8 @@ void BOTAN_FN_ISA_SIMD_4X32 Salsa20::salsa20_simd32_x4(uint8_t output[64 * 4], u R05 += SIMD_4x32::splat(state[5]); R06 += SIMD_4x32::splat(state[6]); R07 += SIMD_4x32::splat(state[7]); - R08 += SIMD_4x32::splat(state[8]) + CTR0; - R09 += SIMD_4x32::splat(state[9]) + CTR1; + R08 += CTR_LO; + R09 += CTR_HI; R10 += SIMD_4x32::splat(state[10]); R11 += SIMD_4x32::splat(state[11]); R12 += SIMD_4x32::splat(state[12]); diff --git a/src/lib/utils/simd/simd_4x32/simd_4x32.h b/src/lib/utils/simd/simd_4x32/simd_4x32.h index 772a0c19479..cdf3aeb66a1 100644 --- a/src/lib/utils/simd/simd_4x32/simd_4x32.h +++ b/src/lib/utils/simd/simd_4x32/simd_4x32.h @@ -746,6 +746,27 @@ class SIMD_4x32 final { #endif } + /** + * Unsigned lane comparison; returns a mask with all bits set in each + * 32-bit lane that is (unsigned) less than the corresponding lane of + * @p other, and all bits cleared otherwise. + */ + SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 unsigned_lt(const SIMD_4x32& other) const noexcept { +#if defined(BOTAN_SIMD_USE_SSSE3) + // No unsigned comparison before AVX-512; bias into the signed domain + const __m128i bias = _mm_set1_epi32(static_cast(0x80000000)); + return SIMD_4x32(_mm_cmpgt_epi32(_mm_xor_si128(other.raw(), bias), _mm_xor_si128(raw(), bias))); +#elif defined(BOTAN_SIMD_USE_ALTIVEC) + return SIMD_4x32(reinterpret_cast<__vector unsigned int>(vec_cmplt(raw(), other.raw()))); +#elif defined(BOTAN_SIMD_USE_NEON) + return SIMD_4x32(vcltq_u32(raw(), other.raw())); +#elif defined(BOTAN_SIMD_USE_LSX) + return SIMD_4x32(__lsx_vslt_wu(raw(), other.raw())); +#elif defined(BOTAN_SIMD_USE_SIMD128) + return SIMD_4x32(wasm_u32x4_lt(raw(), other.raw())); +#endif + } + static inline SIMD_4x32 BOTAN_FN_ISA_SIMD_4X32 choose(const SIMD_4x32& mask, const SIMD_4x32& a, const SIMD_4x32& b) noexcept { diff --git a/src/lib/utils/simd/simd_avx2/simd_avx2.h b/src/lib/utils/simd/simd_avx2/simd_avx2.h index d4d502a6e92..723bc2b097b 100644 --- a/src/lib/utils/simd/simd_avx2/simd_avx2.h +++ b/src/lib/utils/simd/simd_avx2/simd_avx2.h @@ -309,6 +309,17 @@ class SIMD_8x32 final { swap_tops(B3, B7); } + /** + * Unsigned lane comparison; returns a mask with all bits set in each + * 32-bit lane that is (unsigned) less than the corresponding lane of + * @p other, and all bits cleared otherwise. + */ + SIMD_8x32 BOTAN_FN_ISA_AVX2 unsigned_lt(const SIMD_8x32& other) const noexcept { + // No unsigned comparison before AVX-512; bias into the signed domain + const __m256i bias = _mm256_set1_epi32(static_cast(0x80000000)); + return SIMD_8x32(_mm256_cmpgt_epi32(_mm256_xor_si256(other.raw(), bias), _mm256_xor_si256(raw(), bias))); + } + BOTAN_FN_ISA_AVX2 static SIMD_8x32 choose(const SIMD_8x32& mask, const SIMD_8x32& a, const SIMD_8x32& b) noexcept { #if defined(__AVX512VL__) diff --git a/src/lib/utils/simd/simd_avx512/simd_avx512.h b/src/lib/utils/simd/simd_avx512/simd_avx512.h index 22c396ef8df..4d7c872e17b 100644 --- a/src/lib/utils/simd/simd_avx512/simd_avx512.h +++ b/src/lib/utils/simd/simd_avx512/simd_avx512.h @@ -293,6 +293,15 @@ class SIMD_16x32 final { BF.m_avx512 = _mm512_shuffle_i32x4(t7, tf, 0xdd); } + /** + * Unsigned lane comparison; returns a mask with all bits set in each + * 32-bit lane that is (unsigned) less than the corresponding lane of + * @p other, and all bits cleared otherwise. + */ + SIMD_16x32 BOTAN_FN_ISA_AVX512 unsigned_lt(const SIMD_16x32& other) const noexcept { + return SIMD_16x32(_mm512_movm_epi32(_mm512_cmplt_epu32_mask(raw(), other.raw()))); + } + BOTAN_FN_ISA_AVX512 static SIMD_16x32 choose(const SIMD_16x32& mask, const SIMD_16x32& a, const SIMD_16x32& b) { return SIMD_16x32::ternary_fn<0xca>(mask, a, b);