From b32443a2184baff7033853f8fb9321e911783319 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 11 Jun 2024 07:55:32 -0400 Subject: [PATCH 1/3] Small cleanups in P-521 pcurve code --- .../pcurves_secp521r1/pcurves_secp521r1.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp b/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp index 98cbee82bad..293134d0d3a 100644 --- a/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp +++ b/src/lib/math/pcurves/pcurves_secp521r1/pcurves_secp521r1.cpp @@ -12,7 +12,6 @@ namespace Botan::PCurve { namespace { -// clang-format off namespace secp521r1 { template @@ -22,11 +21,7 @@ class P521Rep final { static constexpr size_t N = Params::N; typedef typename Params::W W; - constexpr static std::array one() { - std::array one = {}; - one[0] = 1; - return one; - } + constexpr static std::array one() { return std::array{1}; } constexpr static std::array redc(const std::array& z) { constexpr W TOP_MASK = static_cast(0x1FF); @@ -58,6 +53,7 @@ class P521Rep final { constexpr static std::array from_rep(const std::array& z) { return z; } }; +// clang-format off class Params final : public EllipticCurveParameters< "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", @@ -68,11 +64,11 @@ class Params final : public EllipticCurveParameters< -4> { }; -class Curve final : public EllipticCurve {}; +// clang-format on -} +class Curve final : public EllipticCurve {}; -// clang-format on +} // namespace secp521r1 } // namespace From 3c44844d5f789019331ffd7971944a603c692d1f Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 11 Jun 2024 08:47:40 -0400 Subject: [PATCH 2/3] Skip pcurves_points under the short valgrind run --- src/scripts/ci_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/ci_build.py b/src/scripts/ci_build.py index e7a12722e6c..16b6aeb012c 100755 --- a/src/scripts/ci_build.py +++ b/src/scripts/ci_build.py @@ -252,7 +252,7 @@ def sanitize_kv(some_string): 'ed25519_sign', 'elgamal_decrypt', 'elgamal_encrypt', 'elgamal_keygen', 'ffi_dh', 'ffi_dsa', 'ffi_elgamal', 'frodo_kat_tests', 'hash_nist_mc', 'hss_lms_keygen', 'hss_lms_sign', 'mce_keygen', 'passhash9', 'pbkdf', - 'pwdhash', 'rsa_encrypt', 'rsa_pss', 'rsa_pss_raw', 'scrypt', + 'pcurves_points', 'pwdhash', 'rsa_encrypt', 'rsa_pss', 'rsa_pss_raw', 'scrypt', 'sphincsplus', 'sphincsplus_fors', 'sphincsplus_keygen', 'srp6_kat', 'srp6_rt', 'unit_tls', 'x509_path_bsi', 'x509_path_rsa_pss', 'xmss_keygen', 'xmss_keygen_reference', 'xmss_sign', 'xmss_verify', 'xmss_verify_invalid' From ac87424aa7e9acd48a1e882ed0774440f63f01be Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 11 Jun 2024 08:00:15 -0400 Subject: [PATCH 3/3] Add specialized reduction for secp256k1 This takes advantage of the modulus being near a power of 2. Performance improvement varies by CPU, compiler, and specific algorithm in use, but generally ranges between 10% to 20%. --- src/fuzzer/mp_redc_crandall.cpp | 44 +++++++++++++++ src/lib/math/mp/mp_core.h | 55 ++++++++++++++++++- .../pcurves_secp256k1/pcurves_secp256k1.cpp | 36 ++++++++++-- 3 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 src/fuzzer/mp_redc_crandall.cpp diff --git a/src/fuzzer/mp_redc_crandall.cpp b/src/fuzzer/mp_redc_crandall.cpp new file mode 100644 index 00000000000..1f9f1d3a2b1 --- /dev/null +++ b/src/fuzzer/mp_redc_crandall.cpp @@ -0,0 +1,44 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "mp_fuzzers.h" + +#include +#include + +void fuzz(const uint8_t in[], size_t in_len) { + if(in_len != 8 * sizeof(word)) { + return; + } + +#if BOTAN_MP_WORD_BITS == 64 + // secp256k1 modulus + const word C = 0x1000003d1; +#else + // 128 bit prime with largest possible C + const word C = 0xffffffe1; +#endif + + static const Botan::BigInt refp = Botan::BigInt::power_of_2(4 * BOTAN_MP_WORD_BITS) - C; + static const Botan::BigInt refp2 = refp * refp; + + const auto refz = Botan::BigInt::from_bytes(std::span{in, in_len}); + + if(refz >= refp2) { + return; + } + + const auto refc = refz % refp; + + std::array z = {}; + for(size_t i = 0; i != 8; ++i) { + z[7 - i] = Botan::load_be(in, i); + } + + const auto rc = Botan::redc_crandall(z); + + compare_word_vec(rc.data(), 4, refc._data(), refc.sig_words(), "Crandall reduction"); +} diff --git a/src/lib/math/mp/mp_core.h b/src/lib/math/mp/mp_core.h index bd2c2ff9449..90d947447ab 100644 --- a/src/lib/math/mp/mp_core.h +++ b/src/lib/math/mp/mp_core.h @@ -384,7 +384,7 @@ inline constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], z[i] = word_sub(x[i], p[i], &borrow); } - word_sub(x0, static_cast(0), &borrow); + borrow = (x0 - borrow) > x0; CT::conditional_assign_mem(borrow, z, x, N); } @@ -418,7 +418,7 @@ inline constexpr void bigint_monty_maybe_sub(W z[N], W x0, const W x[N], const W } } - word_sub(x0, static_cast(0), &borrow); + borrow = (x0 - borrow) > x0; CT::conditional_assign_mem(borrow, z, x, N); } @@ -1094,6 +1094,57 @@ void bigint_mul(word z[], void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size); +/** +* Return 2**B - C +*/ +template +consteval std::array crandall_p() { + static_assert(C % 2 == 1); + std::array P; + for(size_t i = 0; i != N; ++i) { + P[i] = WordInfo::max; + } + P[0] = WordInfo::max - (C - 1); + return P; +} + +/** +* Reduce z modulo p = 2**B - C where C is small +* +* z is assumed to be at most (p-1)**2 +* +* For details on the algorithm see +* - Handbook of Applied Cryptography, Algorithm 14.47 +* - Guide to Elliptic Curve Cryptography, Algorithm 2.54 and Note 2.55 +* +*/ +template +constexpr std::array redc_crandall(std::span z) { + static_assert(N >= 2); + + std::array hi = {}; + + // hi = hi * c + lo + + W carry = 0; + for(size_t i = 0; i != N; ++i) { + hi[i] = word_madd3(z[i + N], C, z[i], &carry); + } + + // hi += carry * C + word carry_c[2] = {0}; + carry_c[0] = word_madd2(carry, C, &carry_c[1]); + + carry = bigint_add2_nc(hi.data(), N, carry_c, 2); + + constexpr auto P = crandall_p(); + + std::array r = {}; + bigint_monty_maybe_sub(r.data(), carry, hi.data(), P.data()); + + return r; +} + } // namespace Botan #endif diff --git a/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp b/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp index 7da53aece6e..3f66142da64 100644 --- a/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp +++ b/src/lib/math/pcurves/pcurves_secp256k1/pcurves_secp256k1.cpp @@ -12,9 +12,33 @@ namespace Botan::PCurve { namespace { -// clang-format off namespace secp256k1 { +template +class Secp256k1Rep final { + public: + static constexpr auto P = Params::P; + static constexpr size_t N = Params::N; + typedef typename Params::W W; + + static_assert(WordInfo::bits >= 33); + + static constexpr W C = 0x1000003d1; + + constexpr static std::array one() { return std::array{1}; } + + constexpr static std::array redc(const std::array& z) { + return redc_crandall(std::span{z}); + } + + constexpr static std::array to_rep(const std::array& x) { return x; } + + constexpr static std::array wide_to_rep(const std::array& x) { return redc(x); } + + constexpr static std::array from_rep(const std::array& z) { return z; } +}; + +// clang-format off class Params final : public EllipticCurveParameters< "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", "0", @@ -24,11 +48,15 @@ class Params final : public EllipticCurveParameters< "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"> { }; -class Curve final : public EllipticCurve {}; +// clang-format on -} +#if BOTAN_MP_WORD_BITS == 64 +class Curve final : public EllipticCurve {}; +#else +class Curve final : public EllipticCurve {}; +#endif -// clang-format on +} // namespace secp256k1 } // namespace