From cd40575e34b61537a4bc89eedc40270a34fca089 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 7 Apr 2024 08:47:12 -0400 Subject: [PATCH] Add base point multiplication interface Just for testing right now --- src/lib/pubkey/pcurves/pcurves.cpp | 13 +++++ src/lib/pubkey/pcurves/pcurves.h | 2 + src/lib/pubkey/pcurves/pcurves_impl.h | 82 +++++++++++++++++++++++---- src/lib/pubkey/pcurves/pcurves_util.h | 2 + src/tests/test_ecc_pointmul.cpp | 15 +++++ 5 files changed, 104 insertions(+), 10 deletions(-) diff --git a/src/lib/pubkey/pcurves/pcurves.cpp b/src/lib/pubkey/pcurves/pcurves.cpp index df75e0cb5ee..6a7a1ee452e 100644 --- a/src/lib/pubkey/pcurves/pcurves.cpp +++ b/src/lib/pubkey/pcurves/pcurves.cpp @@ -61,4 +61,17 @@ std::vector hash_to_curve(PrimeOrderCurveId curve, } } +std::vector mul_by_g(PrimeOrderCurveId curve, std::span scalar_bytes) { + switch(curve.code()) { + case PrimeOrderCurveId::P256: + if(auto scalar = P256::Scalar::deserialize(scalar_bytes)) { + return P256::MulByG(*scalar).to_affine().serialize_to_vec(); + } else { + throw Invalid_Argument("Invalid scalar"); + } + default: + throw Not_Implemented("Point mul not implemented for this curve"); + } +} + } // namespace Botan::PCurve diff --git a/src/lib/pubkey/pcurves/pcurves.h b/src/lib/pubkey/pcurves/pcurves.h index 846e9422841..763d244ad39 100644 --- a/src/lib/pubkey/pcurves/pcurves.h +++ b/src/lib/pubkey/pcurves/pcurves.h @@ -59,6 +59,8 @@ std::vector hash_to_curve(PrimeOrderCurveId curve, std::span input, std::span domain_sep); +std::vector BOTAN_TEST_API mul_by_g(PrimeOrderCurveId curve, std::span scalar); + } // namespace Botan::PCurve #endif diff --git a/src/lib/pubkey/pcurves/pcurves_impl.h b/src/lib/pubkey/pcurves/pcurves_impl.h index 99b7ef54411..11fb3192073 100644 --- a/src/lib/pubkey/pcurves/pcurves_impl.h +++ b/src/lib/pubkey/pcurves/pcurves_impl.h @@ -230,8 +230,15 @@ class MontgomeryInteger { } // Returns nullopt if the input is an encoding greater than or equal P - constexpr static std::optional deserialize(std::span bytes) { - const auto words = bytes_to_words(bytes); + constexpr static std::optional deserialize(std::span bytes) { + // We could allow either short inputs or longer zero padded + // inputs here, however it seems best to avoid non-canonical + // representations unless required + if(bytes.size() != Self::BYTES) { + return {}; + } + + const auto words = bytes_to_words(&bytes[0]); if(!bigint_ct_is_lt(words.data(), N, Self::P.data(), N).as_bool()) { return {}; @@ -335,6 +342,11 @@ class AffineCurvePoint { constexpr Self negate() const { return Self(m_x, m_y.negate()); } + std::vector serialize_to_vec() const { + const auto b = this->serialize(); + return std::vector(b.begin(), b.end()); + } + constexpr std::array serialize() const { std::array r = {}; BufferStuffer pack(r); @@ -430,8 +442,54 @@ class ProjectiveCurvePoint { } constexpr static Self add_mixed(const Self& a, const AffinePoint& b) { - // TODO use proper mixed addition formula - return Self::add(a, Self::from_affine(b)); + // TODO avoid these early returns by masking instead + if(a.is_identity()) { + return Self::from_affine(b); + } + + if(b.is_identity()) { + return a; + } + + /* + https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2 + + TODO rename these vars + + TODO reduce vars + + TODO use a complete addition formula??? (YES) + https://eprint.iacr.org/2015/1060.pdf + */ + + const auto Z1Z1 = a.z().square(); + const auto U2 = b.x() * Z1Z1; + const auto S2 = b.y() * a.z() * Z1Z1; + const auto H = U2 - a.x(); + const auto r = S2 - a.y(); + + if(H.is_zero()) { + if(r.is_zero()) { + return a.dbl(); + } else { + return Self::identity(); + } + } + + const auto HH = H.square(); + const auto HHH = H * HH; + const auto V = a.x() * HH; + const auto t2 = r.square(); + const auto t3 = V + V; + const auto t4 = t2 - HHH; + const auto X3 = t4 - t3; + const auto t5 = V - X3; + const auto t6 = a.y() * HHH; + const auto t7 = r * t5; + const auto Y3 = t7 - t6; + const auto Z3 = a.z() * H; + + return Self(X3, Y3, Z3); } constexpr static Self add(const Self& a, const Self& b) { @@ -451,8 +509,6 @@ class ProjectiveCurvePoint { TODO reduce vars - TODO take advantage of A = 0 and A = -3 - TODO use a complete addition formula??? (YES) https://eprint.iacr.org/2015/1060.pdf */ @@ -631,7 +687,7 @@ class PrecomputedMulTable { m_table = ProjectivePoint::to_affine_batch(table); } - constexpr AffinePoint operator()(const Scalar& s) const { + constexpr ProjectivePoint operator()(const Scalar& s) const { const auto bits = s.serialize(); auto accum = ProjectivePoint::identity(); @@ -642,7 +698,7 @@ class PrecomputedMulTable { accum.conditional_add(b_set, m_table[i]); } - return accum.to_affine(); + return accum; } private: @@ -682,6 +738,13 @@ class EllipticCurve { typedef PrecomputedMulTable MulTable; + static const MulTable& MulByGTable() { + static const auto MulG = MulTable(G); + return MulG; + } + + static const ProjectivePoint MulByG(const Scalar& scalar) { return MulByGTable()(scalar); } + // (-B / A), will be zero if A == 0 or B == 0 or Z == 0 static const FieldElement& SSWU_C1() { // We derive it from C2 to avoid a second inversion @@ -745,8 +808,7 @@ inline std::vector hash_to_curve_sswu(std::string_view hash, pt += map_to_curve_sswu(u); } - const auto pt_bytes = pt.to_affine().serialize(); - return std::vector(pt_bytes.begin(), pt_bytes.end()); + return pt.to_affine().serialize_to_vec(); } } // namespace Botan diff --git a/src/lib/pubkey/pcurves/pcurves_util.h b/src/lib/pubkey/pcurves/pcurves_util.h index e57dd42603c..93e7ed0ed59 100644 --- a/src/lib/pubkey/pcurves/pcurves_util.h +++ b/src/lib/pubkey/pcurves/pcurves_util.h @@ -173,6 +173,8 @@ template inline constexpr auto bytes_to_words(const uint8_t bytes[L]) { static_assert(L <= WordInfo::bytes * N); + // TODO: This could be optimized quite a bit which is relevant + // since it executes at runtime std::array r = {}; for(size_t i = 0; i != L; ++i) { shift_left<8>(r); diff --git a/src/tests/test_ecc_pointmul.cpp b/src/tests/test_ecc_pointmul.cpp index 360aabe8728..2cc6c7b2e6d 100644 --- a/src/tests/test_ecc_pointmul.cpp +++ b/src/tests/test_ecc_pointmul.cpp @@ -14,6 +14,10 @@ #include #endif +#if defined(BOTAN_HAS_PCURVES) + #include +#endif + namespace Botan_Tests { namespace { @@ -48,6 +52,17 @@ class ECC_Basepoint_Mul_Tests final : public Text_Based_Test { result.test_eq("p3 affine X", p3.get_affine_x(), X); result.test_eq("p3 affine Y", p3.get_affine_y(), Y); + #if defined(BOTAN_HAS_PCURVES) + if(group_id == "secp256r1") { + const auto mbits = BigInt::encode_1363(m, group.get_order_bytes()); + + const auto pbits = Botan::PCurve::mul_by_g(Botan::PCurve::PrimeOrderCurveId::P256, mbits); + + const Botan::EC_Point pc = group.OS2ECP(pbits); + result.test_eq("pcurves affine X", pc.get_affine_x(), X); + result.test_eq("pcurves affine Y", pc.get_affine_y(), Y); + } + #endif return result; } };