From 8042e9d380805e4cf61383224074358dc59c30b3 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 20 Jun 2024 08:26:24 -0400 Subject: [PATCH] Hide EC Scalar and Point data behind a virtual interface This will allow switching in pcurves later --- src/lib/pubkey/ec_group/ec_apoint.cpp | 102 ++++++------ src/lib/pubkey/ec_group/ec_apoint.h | 32 +++- src/lib/pubkey/ec_group/ec_group.cpp | 58 ++----- src/lib/pubkey/ec_group/ec_group.h | 11 +- src/lib/pubkey/ec_group/ec_inner_bn.cpp | 180 ++++++++++++++++++++++ src/lib/pubkey/ec_group/ec_inner_bn.h | 105 +++++++++++++ src/lib/pubkey/ec_group/ec_inner_data.cpp | 109 +++++++------ src/lib/pubkey/ec_group/ec_inner_data.h | 148 +++++++++--------- src/lib/pubkey/ec_group/ec_scalar.cpp | 76 +++++---- src/lib/pubkey/ec_group/ec_scalar.h | 24 ++- src/lib/pubkey/ec_group/info.txt | 1 + src/lib/pubkey/ec_h2c/ec_h2c.cpp | 13 +- src/lib/pubkey/ec_h2c/ec_h2c.h | 14 ++ src/tests/data/pubkey/ecdsa_verify.vec | 8 - 14 files changed, 599 insertions(+), 282 deletions(-) create mode 100644 src/lib/pubkey/ec_group/ec_inner_bn.cpp create mode 100644 src/lib/pubkey/ec_group/ec_inner_bn.h diff --git a/src/lib/pubkey/ec_group/ec_apoint.cpp b/src/lib/pubkey/ec_group/ec_apoint.cpp index 8d672357cf4..1baa53bfc36 100644 --- a/src/lib/pubkey/ec_group/ec_apoint.cpp +++ b/src/lib/pubkey/ec_group/ec_apoint.cpp @@ -10,90 +10,72 @@ #include #include -#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) - #include -#endif - namespace Botan { -EC_AffinePoint::EC_AffinePoint(std::shared_ptr group, std::unique_ptr point) : - m_group(std::move(group)), m_point(std::move(point)), m_fe_bytes(m_point->field_element_bytes()) {} +EC_AffinePoint::EC_AffinePoint(std::unique_ptr point) : m_point(std::move(point)) { + BOTAN_ASSERT_NONNULL(m_point); +} + +EC_AffinePoint::EC_AffinePoint(const EC_AffinePoint& other) : m_point(other.inner().clone()) {} -EC_AffinePoint::EC_AffinePoint(const EC_AffinePoint& other) : - m_group(other.m_group), - m_point(std::make_unique(other.m_point->value())), - m_fe_bytes(m_point->field_element_bytes()) {} +EC_AffinePoint::EC_AffinePoint(EC_AffinePoint&& other) noexcept : m_point(std::move(other.m_point)) {} -EC_AffinePoint::EC_AffinePoint(EC_AffinePoint&& other) noexcept : - m_group(std::move(other.m_group)), - m_point(std::move(other.m_point)), - m_fe_bytes(m_point->field_element_bytes()) {} +EC_AffinePoint& EC_AffinePoint::operator=(const EC_AffinePoint& other) { + if(this != &other) { + m_point = other.inner().clone(); + } + return (*this); +} + +EC_AffinePoint& EC_AffinePoint::operator=(EC_AffinePoint&& other) noexcept { + m_point.swap(other.m_point); + return (*this); +} + +EC_AffinePoint::EC_AffinePoint(const EC_Group& group, std::span bytes) { + m_point = group._data()->point_deserialize(bytes); + if(!m_point) { + throw Decoding_Error("Failed to deserialize elliptic curve point"); + } +} EC_AffinePoint::EC_AffinePoint(const EC_Group& group, const EC_Point& pt) : - m_group(group._data()), - m_point(std::make_unique(pt)), - m_fe_bytes(m_point->field_element_bytes()) {} + EC_AffinePoint(group, pt.encode(EC_Point_Format::Uncompressed)) {} -EC_AffinePoint::EC_AffinePoint(std::shared_ptr group, EC_Point&& pt) : - m_group(std::move(group)), - m_point(std::make_unique(std::move(pt))), - m_fe_bytes(m_point->field_element_bytes()) {} +size_t EC_AffinePoint::field_element_bytes() const { + return inner().field_element_bytes(); +} EC_AffinePoint EC_AffinePoint::hash_to_curve_ro(const EC_Group& group, std::string_view hash_fn, std::span input, std::span domain_sep) { -#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) - auto pt = hash_to_curve_sswu(group, hash_fn, input, domain_sep, true); - auto v = std::make_unique(std::move(pt)); - return EC_AffinePoint(group._data(), std::move(v)); -#else - BOTAN_UNUSED(group, hash_fn, input, domain_sep); - throw Not_Implemented("Hashing to curve not available in this build"); -#endif + auto pt = group._data()->point_hash_to_curve_ro(hash_fn, input, domain_sep); + return EC_AffinePoint(std::move(pt)); } EC_AffinePoint EC_AffinePoint::hash_to_curve_nu(const EC_Group& group, std::string_view hash_fn, std::span input, std::span domain_sep) { -#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) - auto pt = hash_to_curve_sswu(group, hash_fn, input, domain_sep, false); - auto v = std::make_unique(std::move(pt)); - return EC_AffinePoint(group._data(), std::move(v)); -#else - BOTAN_UNUSED(group, hash_fn, input, domain_sep); - throw Not_Implemented("Hashing to curve not available in this build"); -#endif + auto pt = group._data()->point_hash_to_curve_nu(hash_fn, input, domain_sep); + return EC_AffinePoint(std::move(pt)); } EC_AffinePoint::~EC_AffinePoint() = default; std::optional EC_AffinePoint::deserialize(const EC_Group& group, std::span bytes) { - try { - auto pt = group.OS2ECP(bytes); - auto v = std::make_unique(std::move(pt)); - return EC_AffinePoint(group._data(), std::move(v)); - } catch(...) { - return std::nullopt; - } + auto pt = group._data()->point_deserialize(bytes); + return EC_AffinePoint(std::move(pt)); } EC_AffinePoint EC_AffinePoint::g_mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) { - auto pt = scalar.m_group->blinded_base_point_multiply(scalar.m_scalar->value(), rng, ws); - auto v = std::make_unique(std::move(pt)); - return EC_AffinePoint(scalar.m_group, std::move(v)); + auto pt = scalar._inner().group()->point_g_mul(scalar.inner(), rng, ws); + return EC_AffinePoint(std::move(pt)); } EC_AffinePoint EC_AffinePoint::mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) const { - BOTAN_ARG_CHECK(scalar.m_group == m_group, "Curve mismatch"); - - EC_Point_Var_Point_Precompute mul(m_point->value(), rng, ws); - - const auto order = m_group->order() * m_group->cofactor(); - auto pt = mul.mul(scalar.m_scalar->value(), rng, order, ws); - auto v = std::make_unique(std::move(pt)); - return EC_AffinePoint(m_group, std::move(v)); + return EC_AffinePoint(inner().mul(scalar._inner(), rng, ws)); } void EC_AffinePoint::serialize_x_to(std::span bytes) const { @@ -117,7 +99,15 @@ void EC_AffinePoint::serialize_uncompressed_to(std::span bytes) const { } EC_Point EC_AffinePoint::to_legacy_point() const { - return m_point->value(); + return m_point->to_legacy_point(); +} + +EC_AffinePoint EC_AffinePoint::_from_inner(std::unique_ptr inner) { + return EC_AffinePoint(std::move(inner)); +} + +const std::shared_ptr& EC_AffinePoint::_group() const { + return inner().group(); } } // namespace Botan diff --git a/src/lib/pubkey/ec_group/ec_apoint.h b/src/lib/pubkey/ec_group/ec_apoint.h index 1f9880efbd1..592e06e5d4a 100644 --- a/src/lib/pubkey/ec_group/ec_apoint.h +++ b/src/lib/pubkey/ec_group/ec_apoint.h @@ -24,7 +24,7 @@ class EC_Scalar; class EC_Point; class EC_Group_Data; -class EC_Point_Data; +class EC_AffinePoint_Data; class BOTAN_UNSTABLE_API EC_AffinePoint final { public: @@ -60,7 +60,7 @@ class BOTAN_UNSTABLE_API EC_AffinePoint final { /// Return the number of bytes of a field element /// /// A point consists of two field elements, plus possibly a header - size_t field_element_bytes() const { return m_fe_bytes; } + size_t field_element_bytes() const; /// Write the fixed length encoding of affine x coordinate /// @@ -130,21 +130,37 @@ class BOTAN_UNSTABLE_API EC_AffinePoint final { EC_AffinePoint(const EC_AffinePoint& other); EC_AffinePoint(EC_AffinePoint&& other) noexcept; + EC_AffinePoint& operator=(const EC_AffinePoint& other); + EC_AffinePoint& operator=(EC_AffinePoint&& other) noexcept; + + EC_AffinePoint(const EC_Group& group, std::span bytes); + + /** + * Deprecated conversion + */ EC_AffinePoint(const EC_Group& group, const EC_Point& pt); - EC_AffinePoint(const std::shared_ptr, EC_Point&& pt); + /** + * Deprecated conversion + */ EC_Point to_legacy_point() const; ~EC_AffinePoint(); + const EC_AffinePoint_Data& _inner() const { return inner(); } + + static EC_AffinePoint _from_inner(std::unique_ptr inner); + + const std::shared_ptr& _group() const; + private: - friend class EC_Mul2Table_Data; + friend class EC_Mul2Table; + + EC_AffinePoint(std::unique_ptr point); - EC_AffinePoint(std::shared_ptr group, std::unique_ptr point); + const EC_AffinePoint_Data& inner() const { return *m_point; } - std::shared_ptr m_group; - std::unique_ptr m_point; - const size_t m_fe_bytes; + std::unique_ptr m_point; }; } // namespace Botan diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp index 8d2cfc0adeb..2d66d313705 100644 --- a/src/lib/pubkey/ec_group/ec_group.cpp +++ b/src/lib/pubkey/ec_group/ec_group.cpp @@ -736,63 +736,35 @@ bool EC_Group::verify_group(RandomNumberGenerator& rng, bool strong) const { return true; } -class EC_Mul2Table_Data final { - public: - EC_Mul2Table_Data(std::shared_ptr group, const EC_Point& g, const EC_Point& h) : - m_group(std::move(group)), m_tbl(g, h) {} - - EC_Mul2Table_Data(const EC_AffinePoint& h) : - m_group(h.m_group), m_tbl(h.m_group->base_point(), h.to_legacy_point()) {} - - std::optional mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const { - auto pt = m_tbl.multi_exp(x.m_scalar->value(), y.m_scalar->value()); - - if(pt.is_zero()) { - return std::nullopt; - } - return EC_AffinePoint(m_group, std::move(pt)); - } - - std::optional mul2_vartime_x_mod_order(const EC_Scalar& x, const EC_Scalar& y) const { - auto pt = m_tbl.multi_exp(x.m_scalar->value(), y.m_scalar->value()); - - if(pt.is_zero()) { - return std::nullopt; - } - auto v = std::make_unique(m_group->mod_order(pt.get_affine_x())); - return EC_Scalar(m_group, std::move(v)); - } - - std::optional mul2_vartime_x_mod_order(const EC_Scalar& c, - const EC_Scalar& x, - const EC_Scalar& y) const { - return this->mul2_vartime_x_mod_order(c * x, c * y); - } - - private: - std::shared_ptr m_group; - EC_Point_Multi_Point_Precompute m_tbl; -}; - EC_Group::Mul2Table::Mul2Table(const EC_Group& group, const EC_Point& h) : - m_tbl(std::make_unique(group.m_data, group.generator(), h)) {} + EC_Group::Mul2Table(EC_AffinePoint(group, h)) {} -EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(std::make_unique(h)) {} +EC_Group::Mul2Table::Mul2Table(const EC_AffinePoint& h) : m_tbl(h._group()->make_mul2_table(h._inner())) {} EC_Group::Mul2Table::~Mul2Table() = default; std::optional EC_Group::Mul2Table::mul2_vartime(const EC_Scalar& x, const EC_Scalar& y) const { - return m_tbl->mul2_vartime(x, y); + auto pt = m_tbl->mul2_vartime(x._inner(), y._inner()); + if(pt) { + return EC_AffinePoint::_from_inner(std::move(pt)); + } else { + return {}; + } } std::optional EC_Group::Mul2Table::mul2_vartime_x_mod_order(const EC_Scalar& x, const EC_Scalar& y) const { - return m_tbl->mul2_vartime_x_mod_order(x, y); + auto s = m_tbl->mul2_vartime_x_mod_order(x._inner(), y._inner()); + if(s) { + return EC_Scalar::_from_inner(std::move(s)); + } else { + return {}; + } } std::optional EC_Group::Mul2Table::mul2_vartime_x_mod_order(const EC_Scalar& c, const EC_Scalar& x, const EC_Scalar& y) const { - return m_tbl->mul2_vartime_x_mod_order(c, x, y); + return this->mul2_vartime_x_mod_order(c * x, c * y); } } // namespace Botan diff --git a/src/lib/pubkey/ec_group/ec_group.h b/src/lib/pubkey/ec_group/ec_group.h index e44d896f6f2..9bdd063e3e7 100644 --- a/src/lib/pubkey/ec_group/ec_group.h +++ b/src/lib/pubkey/ec_group/ec_group.h @@ -219,10 +219,15 @@ class BOTAN_PUBLIC_API(2, 0) EC_Group final { class Mul2Table final { public: /** - + * Internal transition function + * + * @warning this will be removed in 3.6.0, NOT COVERED BY SEMVER */ Mul2Table(const EC_Group& group, const EC_Point& h); + /** + * Create a table for computing g*x + h*y + */ Mul2Table(const EC_AffinePoint& h); /** @@ -254,7 +259,7 @@ class BOTAN_PUBLIC_API(2, 0) EC_Group final { * * Returns nullopt if g*x*c + h*y*c was the point at infinity * - * @warning this function is variable time with respect to x and y + * @warning this function is variable time with respect to c, x and y */ std::optional mul2_vartime_x_mod_order(const EC_Scalar& c, const EC_Scalar& x, @@ -521,7 +526,7 @@ class BOTAN_PUBLIC_API(2, 0) EC_Group final { /* * For internal use only */ - std::shared_ptr _data() const { return m_data; } + const std::shared_ptr& _data() const { return m_data; } private: static EC_Group_Data_Map& ec_group_data(); diff --git a/src/lib/pubkey/ec_group/ec_inner_bn.cpp b/src/lib/pubkey/ec_group/ec_inner_bn.cpp new file mode 100644 index 00000000000..710fec32063 --- /dev/null +++ b/src/lib/pubkey/ec_group/ec_inner_bn.cpp @@ -0,0 +1,180 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +namespace Botan { + +const EC_Scalar_Data_BN& EC_Scalar_Data_BN::checked_ref(const EC_Scalar_Data& data) { + const auto* p = dynamic_cast(&data); + if(!p) { + throw Invalid_State("Failed conversion to EC_Scalar_Data_BN"); + } + return *p; +} + +const std::shared_ptr& EC_Scalar_Data_BN::group() const { + return m_group; +} + +size_t EC_Scalar_Data_BN::bytes() const { + return this->group()->order_bytes(); +} + +std::unique_ptr EC_Scalar_Data_BN::clone() const { + return std::make_unique(this->group(), this->value()); +} + +bool EC_Scalar_Data_BN::is_zero() const { + return this->value().is_zero(); +} + +bool EC_Scalar_Data_BN::is_eq(const EC_Scalar_Data& other) const { + return (value() == checked_ref(other).value()); +} + +void EC_Scalar_Data_BN::assign(const EC_Scalar_Data& other) { + m_v = checked_ref(other).value(); +} + +void EC_Scalar_Data_BN::square_self() { + m_group->square_mod_order(m_v); +} + +std::unique_ptr EC_Scalar_Data_BN::negate() const { + return std::make_unique(m_group, m_group->mod_order(-m_v)); +} + +std::unique_ptr EC_Scalar_Data_BN::invert() const { + return std::make_unique(m_group, m_group->inverse_mod_order(m_v)); +} + +std::unique_ptr EC_Scalar_Data_BN::add(const EC_Scalar_Data& other) const { + return std::make_unique(m_group, m_group->mod_order(m_v + checked_ref(other).value())); +} + +std::unique_ptr EC_Scalar_Data_BN::sub(const EC_Scalar_Data& other) const { + return std::make_unique(m_group, m_group->mod_order(m_v - checked_ref(other).value())); +} + +std::unique_ptr EC_Scalar_Data_BN::mul(const EC_Scalar_Data& other) const { + return std::make_unique(m_group, m_group->multiply_mod_order(m_v, checked_ref(other).value())); +} + +void EC_Scalar_Data_BN::serialize_to(std::span bytes) const { + BOTAN_ARG_CHECK(bytes.size() == m_group->order_bytes(), "Invalid output length"); + m_v.serialize_to(bytes); +} + +EC_AffinePoint_Data_BN::EC_AffinePoint_Data_BN(std::shared_ptr group, EC_Point pt) : + m_group(std::move(group)), m_pt(std::move(pt)) { + m_pt.force_affine(); + m_xy = m_pt.xy_bytes(); +} + +EC_AffinePoint_Data_BN::EC_AffinePoint_Data_BN(std::shared_ptr group, + std::span pt) : + m_group(std::move(group)) { + BOTAN_ASSERT_NONNULL(m_group); + m_pt = Botan::OS2ECP(pt.data(), pt.size(), m_group->curve()); + m_xy = m_pt.xy_bytes(); +} + +std::unique_ptr EC_AffinePoint_Data_BN::clone() const { + return std::make_unique(m_group, m_pt); +} + +const std::shared_ptr& EC_AffinePoint_Data_BN::group() const { + return m_group; +} + +std::unique_ptr EC_AffinePoint_Data_BN::mul(const EC_Scalar_Data& scalar, + RandomNumberGenerator& rng, + std::vector& ws) const { + BOTAN_ARG_CHECK(scalar.group() == m_group, "Curve mismatch"); + const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar); + + EC_Point_Var_Point_Precompute mul(m_pt, rng, ws); + + const auto order = m_group->order() * m_group->cofactor(); + auto pt = mul.mul(bn.value(), rng, order, ws); + return std::make_unique(m_group, std::move(pt)); +} + +size_t EC_AffinePoint_Data_BN::field_element_bytes() const { + return m_xy.size() / 2; +} + +void EC_AffinePoint_Data_BN::serialize_x_to(std::span bytes) const { + const size_t fe_bytes = this->field_element_bytes(); + BOTAN_ARG_CHECK(bytes.size() == fe_bytes, "Invalid output size"); + copy_mem(bytes, std::span{m_xy}.first(fe_bytes)); +} + +void EC_AffinePoint_Data_BN::serialize_y_to(std::span bytes) const { + const size_t fe_bytes = this->field_element_bytes(); + BOTAN_ARG_CHECK(bytes.size() == fe_bytes, "Invalid output size"); + copy_mem(bytes, std::span{m_xy}.last(fe_bytes)); +} + +void EC_AffinePoint_Data_BN::serialize_xy_to(std::span bytes) const { + const size_t fe_bytes = this->field_element_bytes(); + BOTAN_ARG_CHECK(bytes.size() == 2 * fe_bytes, "Invalid output size"); + copy_mem(bytes, m_xy); +} + +void EC_AffinePoint_Data_BN::serialize_compressed_to(std::span bytes) const { + const size_t fe_bytes = this->field_element_bytes(); + BOTAN_ARG_CHECK(bytes.size() == 1 + fe_bytes, "Invalid output size"); + const bool y_is_odd = (m_xy[m_xy.size() - 1] & 0x01) == 0x01; + + BufferStuffer stuffer(bytes); + stuffer.append(y_is_odd ? 0x03 : 0x02); + serialize_x_to(stuffer.next(fe_bytes)); +} + +void EC_AffinePoint_Data_BN::serialize_uncompressed_to(std::span bytes) const { + const size_t fe_bytes = this->field_element_bytes(); + BOTAN_ARG_CHECK(bytes.size() == 1 + 2 * fe_bytes, "Invalid output size"); + BufferStuffer stuffer(bytes); + stuffer.append(0x04); + stuffer.append(m_xy); +} + +EC_Mul2Table_Data_BN::EC_Mul2Table_Data_BN(const EC_AffinePoint_Data& g, const EC_AffinePoint_Data& h) : + m_group(g.group()), m_tbl(g.to_legacy_point(), h.to_legacy_point()) { + BOTAN_ARG_CHECK(h.group() == m_group, "Curve mismatch"); +} + +std::unique_ptr EC_Mul2Table_Data_BN::mul2_vartime(const EC_Scalar_Data& x, + const EC_Scalar_Data& y) const { + BOTAN_ARG_CHECK(x.group() == m_group && y.group() == m_group, "Curve mismatch"); + + const auto& bn_x = EC_Scalar_Data_BN::checked_ref(x); + const auto& bn_y = EC_Scalar_Data_BN::checked_ref(y); + auto pt = m_tbl.multi_exp(bn_x.value(), bn_y.value()); + + if(pt.is_zero()) { + return nullptr; + } + return std::make_unique(m_group, std::move(pt)); +} + +std::unique_ptr EC_Mul2Table_Data_BN::mul2_vartime_x_mod_order(const EC_Scalar_Data& x, + const EC_Scalar_Data& y) const { + BOTAN_ARG_CHECK(x.group() == m_group && y.group() == m_group, "Curve mismatch"); + + const auto& bn_x = EC_Scalar_Data_BN::checked_ref(x); + const auto& bn_y = EC_Scalar_Data_BN::checked_ref(y); + auto pt = m_tbl.multi_exp(bn_x.value(), bn_y.value()); + + if(pt.is_zero()) { + return nullptr; + } + return std::make_unique(m_group, m_group->mod_order(pt.get_affine_x())); +} + +} // namespace Botan diff --git a/src/lib/pubkey/ec_group/ec_inner_bn.h b/src/lib/pubkey/ec_group/ec_inner_bn.h new file mode 100644 index 00000000000..a130ccf263a --- /dev/null +++ b/src/lib/pubkey/ec_group/ec_inner_bn.h @@ -0,0 +1,105 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_EC_INNER_DATA_BN_H_ +#define BOTAN_EC_INNER_DATA_BN_H_ + +#include + +namespace Botan { + +class EC_Scalar_Data_BN final : public EC_Scalar_Data { + public: + EC_Scalar_Data_BN(std::shared_ptr group, BigInt v) : + m_group(std::move(group)), m_v(std::move(v)) {} + + static const EC_Scalar_Data_BN& checked_ref(const EC_Scalar_Data& data); + + const std::shared_ptr& group() const override; + + std::unique_ptr clone() const override; + + size_t bytes() const override; + + bool is_zero() const override; + + bool is_eq(const EC_Scalar_Data& y) const override; + + void assign(const EC_Scalar_Data& y) override; + + void square_self() override; + + std::unique_ptr negate() const override; + + std::unique_ptr invert() const override; + + std::unique_ptr add(const EC_Scalar_Data& other) const override; + + std::unique_ptr sub(const EC_Scalar_Data& other) const override; + + std::unique_ptr mul(const EC_Scalar_Data& other) const override; + + void serialize_to(std::span bytes) const override; + + const BigInt& value() const { return m_v; } + + private: + std::shared_ptr m_group; + BigInt m_v; +}; + +class EC_AffinePoint_Data_BN final : public EC_AffinePoint_Data { + public: + EC_AffinePoint_Data_BN(std::shared_ptr group, EC_Point pt); + + EC_AffinePoint_Data_BN(std::shared_ptr group, std::span pt); + + const std::shared_ptr& group() const override; + + std::unique_ptr clone() const override; + + size_t field_element_bytes() const override; + + void serialize_x_to(std::span bytes) const override; + + void serialize_y_to(std::span bytes) const override; + + void serialize_xy_to(std::span bytes) const override; + + void serialize_compressed_to(std::span bytes) const override; + + void serialize_uncompressed_to(std::span bytes) const override; + + std::unique_ptr mul(const EC_Scalar_Data& scalar, + RandomNumberGenerator& rng, + std::vector& ws) const override; + + EC_Point to_legacy_point() const override { return m_pt; } + + private: + std::shared_ptr m_group; + EC_Point m_pt; + secure_vector m_xy; +}; + +class EC_Mul2Table_Data_BN final : public EC_Mul2Table_Data { + public: + EC_Mul2Table_Data_BN(const EC_AffinePoint_Data& g, const EC_AffinePoint_Data& h); + + std::unique_ptr mul2_vartime(const EC_Scalar_Data& x, + const EC_Scalar_Data& y) const override; + + std::unique_ptr mul2_vartime_x_mod_order(const EC_Scalar_Data& x, + const EC_Scalar_Data& y) const override; + + private: + std::shared_ptr m_group; + EC_Point_Multi_Point_Precompute m_tbl; +}; + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/ec_group/ec_inner_data.cpp b/src/lib/pubkey/ec_group/ec_inner_data.cpp index 7fb40f76030..1ec53cf9519 100644 --- a/src/lib/pubkey/ec_group/ec_inner_data.cpp +++ b/src/lib/pubkey/ec_group/ec_inner_data.cpp @@ -6,6 +6,12 @@ #include +#include + +#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) + #include +#endif + namespace Botan { EC_Group_Data::EC_Group_Data(const BigInt& p, @@ -51,76 +57,41 @@ bool EC_Group_Data::params_match(const EC_Group_Data& other) const { std::unique_ptr EC_Group_Data::scalar_from_bytes_with_trunc(std::span bytes) const { auto bn = BigInt::from_bytes_with_max_bits(bytes.data(), bytes.size(), m_order_bits); - return std::make_unique(mod_order(bn)); + return std::make_unique(shared_from_this(), mod_order(bn)); } std::unique_ptr EC_Group_Data::scalar_from_bytes_mod_order(std::span bytes) const { BOTAN_ARG_CHECK(bytes.size() <= 2 * order_bytes(), "Input too large"); - return std::make_unique(mod_order(BigInt(bytes))); + return std::make_unique(shared_from_this(), mod_order(BigInt(bytes))); } std::unique_ptr EC_Group_Data::scalar_random(RandomNumberGenerator& rng) const { - return std::make_unique(BigInt::random_integer(rng, BigInt::one(), m_order)); -} - -bool EC_Group_Data::scalar_is_zero(const EC_Scalar_Data& s) const { - return s.value().is_zero(); -} - -bool EC_Group_Data::scalar_is_eq(const EC_Scalar_Data& x, const EC_Scalar_Data& y) const { - return x.value() == y.value(); + return std::make_unique(shared_from_this(), BigInt::random_integer(rng, BigInt::one(), m_order)); } std::unique_ptr EC_Group_Data::scalar_zero() const { - return std::make_unique(BigInt::zero()); + return std::make_unique(shared_from_this(), BigInt::zero()); } std::unique_ptr EC_Group_Data::scalar_one() const { - return std::make_unique(BigInt::one()); -} - -std::unique_ptr EC_Group_Data::scalar_invert(const EC_Scalar_Data& s) const { - return std::make_unique(inverse_mod_order(s.value())); -} - -void EC_Group_Data::scalar_assign(EC_Scalar_Data& x, const EC_Scalar_Data& y) const { - x.set_value(y.value()); -} - -void EC_Group_Data::scalar_square_self(EC_Scalar_Data& s) const { - s.set_value(square_mod_order(s.value())); -} - -std::unique_ptr EC_Group_Data::scalar_negate(const EC_Scalar_Data& s) const { - return std::make_unique(mod_order(-s.value())); -} - -std::unique_ptr EC_Group_Data::scalar_add(const EC_Scalar_Data& a, const EC_Scalar_Data& b) const { - return std::make_unique(mod_order(a.value() + b.value())); -} - -std::unique_ptr EC_Group_Data::scalar_sub(const EC_Scalar_Data& a, const EC_Scalar_Data& b) const { - return std::make_unique(mod_order(a.value() - b.value())); -} - -std::unique_ptr EC_Group_Data::scalar_mul(const EC_Scalar_Data& a, const EC_Scalar_Data& b) const { - return std::make_unique(multiply_mod_order(a.value(), b.value())); + return std::make_unique(shared_from_this(), BigInt::one()); } std::unique_ptr EC_Group_Data::scalar_from_bigint(const BigInt& bn) const { // Assumed to have been already checked as in range - return std::make_unique(bn); + return std::make_unique(shared_from_this(), bn); } std::unique_ptr EC_Group_Data::gk_x_mod_order(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng, std::vector& ws) const { - const auto pt = m_base_mult.mul(scalar.value(), rng, m_order, ws); + const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar); + const auto pt = m_base_mult.mul(bn.value(), rng, m_order, ws); if(pt.is_zero()) { return scalar_zero(); } else { - return std::make_unique(mod_order(pt.get_affine_x())); + return std::make_unique(shared_from_this(), mod_order(pt.get_affine_x())); } } @@ -135,12 +106,54 @@ std::unique_ptr EC_Group_Data::scalar_deserialize(std::span(std::move(r)); + return std::make_unique(shared_from_this(), std::move(r)); +} + +std::unique_ptr EC_Group_Data::point_deserialize(std::span bytes) const { + try { + auto pt = Botan::OS2ECP(bytes.data(), bytes.size(), curve()); + return std::make_unique(shared_from_this(), std::move(pt)); + } catch(...) { + return nullptr; + } } -void EC_Group_Data::scalar_serialize_to(const EC_Scalar_Data& s, std::span bytes) const { - BOTAN_ARG_CHECK(bytes.size() == m_order_bytes, "Invalid output length"); - s.value().serialize_to(bytes); +std::unique_ptr EC_Group_Data::point_hash_to_curve_ro(std::string_view hash_fn, + std::span input, + std::span domain_sep) const { +#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) + auto pt = hash_to_curve_sswu(*this, hash_fn, input, domain_sep, true); + return std::make_unique(shared_from_this(), std::move(pt)); +#else + BOTAN_UNUSED(hash_fn, input, domain_sep); + throw Not_Implemented("Hashing to curve not available in this build"); +#endif +} + +std::unique_ptr EC_Group_Data::point_hash_to_curve_nu(std::string_view hash_fn, + std::span input, + std::span domain_sep) const { +#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) + auto pt = hash_to_curve_sswu(*this, hash_fn, input, domain_sep, false); + return std::make_unique(shared_from_this(), std::move(pt)); +#else + BOTAN_UNUSED(hash_fn, input, domain_sep); + throw Not_Implemented("Hashing to curve not available in this build"); +#endif +} + +std::unique_ptr EC_Group_Data::point_g_mul(const EC_Scalar_Data& scalar, + RandomNumberGenerator& rng, + std::vector& ws) const { + const auto& group = scalar.group(); + const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar); + auto pt = group->blinded_base_point_multiply(bn.value(), rng, ws); + return std::make_unique(shared_from_this(), std::move(pt)); +} + +std::unique_ptr EC_Group_Data::make_mul2_table(const EC_AffinePoint_Data& h) const { + EC_AffinePoint_Data_BN g(shared_from_this(), this->base_point()); + return std::make_unique(g, h); } } // namespace Botan diff --git a/src/lib/pubkey/ec_group/ec_inner_data.h b/src/lib/pubkey/ec_group/ec_inner_data.h index a036e3d5bc8..c6462ebbaea 100644 --- a/src/lib/pubkey/ec_group/ec_inner_data.h +++ b/src/lib/pubkey/ec_group/ec_inner_data.h @@ -14,77 +14,91 @@ #include #include #include +#include #include namespace Botan { -class EC_Scalar_Data final { +class EC_Group_Data; + +class EC_Scalar_Data { public: - EC_Scalar_Data(BigInt v) : m_v(std::move(v)) {} + virtual ~EC_Scalar_Data() = default; - std::unique_ptr clone() const { return std::make_unique(m_v); } + virtual const std::shared_ptr& group() const = 0; - const BigInt& value() const { return m_v; } + virtual size_t bytes() const = 0; - void set_value(const BigInt& v) { m_v = v; } + virtual std::unique_ptr clone() const = 0; - private: - BigInt m_v; + virtual bool is_zero() const = 0; + + virtual bool is_eq(const EC_Scalar_Data& y) const = 0; + + virtual void assign(const EC_Scalar_Data& y) = 0; + + virtual void square_self() = 0; + + virtual std::unique_ptr negate() const = 0; + + virtual std::unique_ptr invert() const = 0; + + virtual std::unique_ptr add(const EC_Scalar_Data& other) const = 0; + + virtual std::unique_ptr sub(const EC_Scalar_Data& other) const = 0; + + virtual std::unique_ptr mul(const EC_Scalar_Data& other) const = 0; + + virtual void serialize_to(std::span bytes) const = 0; }; -class EC_Point_Data final { +class EC_AffinePoint_Data { public: - EC_Point_Data(EC_Point pt) : m_pt(std::move(pt)) { - m_pt.force_affine(); - m_xy = m_pt.xy_bytes(); - } + virtual ~EC_AffinePoint_Data() = default; - const EC_Point& value() const { return m_pt; } + virtual const std::shared_ptr& group() const = 0; - size_t field_element_bytes() const { return m_xy.size() / 2; } + virtual std::unique_ptr clone() const = 0; - void serialize_x_to(std::span bytes) const { - const size_t fe_bytes = field_element_bytes(); - BOTAN_ARG_CHECK(bytes.size() == fe_bytes, "Invalid output size"); - copy_mem(bytes, std::span{m_xy}.first(fe_bytes)); - } + // Return size of a field element + virtual size_t field_element_bytes() const = 0; - void serialize_y_to(std::span bytes) const { - const size_t fe_bytes = field_element_bytes(); - BOTAN_ARG_CHECK(bytes.size() == fe_bytes, "Invalid output size"); - copy_mem(bytes, std::span{m_xy}.last(fe_bytes)); - } + // Writes 1 field element worth of data to bytes + virtual void serialize_x_to(std::span bytes) const = 0; - void serialize_xy_to(std::span bytes) const { - const size_t fe_bytes = field_element_bytes(); - BOTAN_ARG_CHECK(bytes.size() == 2 * fe_bytes, "Invalid output size"); - copy_mem(bytes, m_xy); - } + // Writes 1 field element worth of data to bytes + virtual void serialize_y_to(std::span bytes) const = 0; - void serialize_compressed_to(std::span bytes) const { - const size_t fe_bytes = field_element_bytes(); - BOTAN_ARG_CHECK(bytes.size() == 1 + fe_bytes, "Invalid output size"); - const bool y_is_odd = (m_xy[m_xy.size() - 1] & 0x01) == 0x01; + // Writes 2 field elements worth of data to bytes + virtual void serialize_xy_to(std::span bytes) const = 0; - BufferStuffer stuffer(bytes); - stuffer.append(y_is_odd ? 0x03 : 0x02); - serialize_x_to(stuffer.next(fe_bytes)); - } + // Writes 1 byte + 1 field element worth of data to bytes + virtual void serialize_compressed_to(std::span bytes) const = 0; - void serialize_uncompressed_to(std::span bytes) const { - const size_t fe_bytes = field_element_bytes(); - BOTAN_ARG_CHECK(bytes.size() == 1 + 2 * fe_bytes, "Invalid output size"); - BufferStuffer stuffer(bytes); - stuffer.append(0x04); - stuffer.append(m_xy); - } + // Writes 1 byte + 2 field elements worth of data to bytes + virtual void serialize_uncompressed_to(std::span bytes) const = 0; - private: - EC_Point m_pt; - secure_vector m_xy; + virtual std::unique_ptr mul(const EC_Scalar_Data& scalar, + RandomNumberGenerator& rng, + std::vector& ws) const = 0; + + virtual EC_Point to_legacy_point() const = 0; }; -class EC_Group_Data final { +class EC_Mul2Table_Data { + public: + virtual ~EC_Mul2Table_Data() = default; + + // Returns nullptr if g*x + h*y was point at infinity + virtual std::unique_ptr mul2_vartime(const EC_Scalar_Data& x, + const EC_Scalar_Data& y) const = 0; + + // Returns nullptr if g*x + h*y was point at infinity + virtual std::unique_ptr mul2_vartime_x_mod_order(const EC_Scalar_Data& x, + const EC_Scalar_Data& y) const = 0; +}; + +class EC_Group_Data final : public std::enable_shared_from_this { public: EC_Group_Data(const BigInt& p, const BigInt& a, @@ -167,39 +181,35 @@ class EC_Group_Data final { std::unique_ptr scalar_from_bytes_mod_order(std::span bytes) const; - std::unique_ptr scalar_random(RandomNumberGenerator& rng) const; - - bool scalar_is_zero(const EC_Scalar_Data& s) const; + std::unique_ptr scalar_from_bigint(const BigInt& bn) const; - bool scalar_is_eq(const EC_Scalar_Data& x, const EC_Scalar_Data& y) const; + std::unique_ptr scalar_random(RandomNumberGenerator& rng) const; std::unique_ptr scalar_zero() const; std::unique_ptr scalar_one() const; - std::unique_ptr scalar_invert(const EC_Scalar_Data& s) const; - - void scalar_assign(EC_Scalar_Data& x, const EC_Scalar_Data& y) const; - - void scalar_square_self(EC_Scalar_Data& s) const; - - std::unique_ptr scalar_negate(const EC_Scalar_Data& s) const; - - std::unique_ptr scalar_add(const EC_Scalar_Data& a, const EC_Scalar_Data& b) const; - - std::unique_ptr scalar_sub(const EC_Scalar_Data& a, const EC_Scalar_Data& b) const; - - std::unique_ptr scalar_mul(const EC_Scalar_Data& a, const EC_Scalar_Data& b) const; - - std::unique_ptr scalar_from_bigint(const BigInt& bn) const; - std::unique_ptr gk_x_mod_order(const EC_Scalar_Data& scalar, RandomNumberGenerator& rng, std::vector& ws) const; std::unique_ptr scalar_deserialize(std::span bytes); - void scalar_serialize_to(const EC_Scalar_Data& s, std::span bytes) const; + std::unique_ptr point_deserialize(std::span bytes) const; + + std::unique_ptr point_hash_to_curve_ro(std::string_view hash_fn, + std::span input, + std::span domain_sep) const; + + std::unique_ptr point_hash_to_curve_nu(std::string_view hash_fn, + std::span input, + std::span domain_sep) const; + + std::unique_ptr point_g_mul(const EC_Scalar_Data& scalar, + RandomNumberGenerator& rng, + std::vector& ws) const; + + std::unique_ptr make_mul2_table(const EC_AffinePoint_Data& pt) const; private: CurveGFp m_curve; diff --git a/src/lib/pubkey/ec_group/ec_scalar.cpp b/src/lib/pubkey/ec_group/ec_scalar.cpp index dffc1693b76..7021b76b601 100644 --- a/src/lib/pubkey/ec_group/ec_scalar.cpp +++ b/src/lib/pubkey/ec_group/ec_scalar.cpp @@ -11,52 +11,69 @@ namespace Botan { -EC_Scalar::EC_Scalar(std::shared_ptr group, std::unique_ptr scalar) : - m_group(std::move(group)), m_scalar(std::move(scalar)), m_scalar_bytes(m_group->order_bytes()) {} +EC_Scalar EC_Scalar::_from_inner(std::unique_ptr inner) { + return EC_Scalar(std::move(inner)); +} + +EC_Scalar::EC_Scalar(std::unique_ptr scalar) : m_scalar(std::move(scalar)) { + BOTAN_ASSERT_NONNULL(m_scalar); +} -EC_Scalar::EC_Scalar(const EC_Group& group, std::unique_ptr scalar) : - m_group(group._data()), m_scalar(std::move(scalar)), m_scalar_bytes(m_group->order_bytes()) {} +EC_Scalar::EC_Scalar(const EC_Scalar& other) : m_scalar(other.inner().clone()) {} -EC_Scalar::EC_Scalar(const EC_Scalar& other) : - m_group(other.m_group), m_scalar(other.inner().clone()), m_scalar_bytes(m_group->order_bytes()) {} +EC_Scalar::EC_Scalar(EC_Scalar&& other) noexcept : m_scalar(std::move(other.m_scalar)) {} -EC_Scalar::EC_Scalar(EC_Scalar&& other) noexcept : - m_group(std::move(other.m_group)), m_scalar(std::move(other.m_scalar)), m_scalar_bytes(m_group->order_bytes()) {} +EC_Scalar& EC_Scalar::operator=(const EC_Scalar& other) { + if(this != &other) { + this->assign(other); + } + return (*this); +} + +EC_Scalar& EC_Scalar::operator=(EC_Scalar&& other) noexcept { + BOTAN_ARG_CHECK(_inner().group() == other._inner().group(), "Curve mismatch"); + std::swap(m_scalar, other.m_scalar); + return (*this); +} EC_Scalar::~EC_Scalar() = default; +size_t EC_Scalar::bytes() const { + return m_scalar->bytes(); +} + EC_Scalar EC_Scalar::from_bytes_with_trunc(const EC_Group& group, std::span bytes) { - return EC_Scalar(group, group._data()->scalar_from_bytes_with_trunc(bytes)); + return EC_Scalar(group._data()->scalar_from_bytes_with_trunc(bytes)); } EC_Scalar EC_Scalar::from_bytes_mod_order(const EC_Group& group, std::span bytes) { - return EC_Scalar(group, group._data()->scalar_from_bytes_mod_order(bytes)); + return EC_Scalar(group._data()->scalar_from_bytes_mod_order(bytes)); } EC_Scalar EC_Scalar::random(const EC_Group& group, RandomNumberGenerator& rng) { - return EC_Scalar(group, group._data()->scalar_random(rng)); + return EC_Scalar(group._data()->scalar_random(rng)); } EC_Scalar EC_Scalar::one(const EC_Group& group) { - return EC_Scalar(group, group._data()->scalar_one()); + return EC_Scalar(group._data()->scalar_one()); } EC_Scalar EC_Scalar::from_bigint(const EC_Group& group, const BigInt& bn) { BOTAN_ARG_CHECK(bn.is_positive() && bn <= group._data()->order(), "EC_Scalar::from_bigint out of range"); - return EC_Scalar(group, group._data()->scalar_from_bigint(bn)); + return EC_Scalar(group._data()->scalar_from_bigint(bn)); } EC_Scalar EC_Scalar::gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws) { - const auto& group = scalar.group(); - return EC_Scalar(group, group->gk_x_mod_order(scalar.inner(), rng, ws)); + const auto& group = scalar._inner().group(); + return EC_Scalar(group->gk_x_mod_order(scalar.inner(), rng, ws)); } void EC_Scalar::serialize_to(std::span bytes) const { - group()->scalar_serialize_to(inner(), bytes); + inner().serialize_to(bytes); } void EC_Scalar::serialize_pair_to(std::span bytes, const EC_Scalar& r, const EC_Scalar& s) { - BOTAN_ARG_CHECK(r.group() == s.group(), "Curve mismatch"); + BOTAN_ARG_CHECK(r._inner().group() == s._inner().group(), "Curve mismatch"); const size_t scalar_bytes = r.bytes(); BOTAN_ARG_CHECK(bytes.size() == 2 * scalar_bytes, "Invalid output length"); r.serialize_to(bytes.first(scalar_bytes)); @@ -83,51 +100,46 @@ std::optional> EC_Scalar::deserialize_pair(const std::optional EC_Scalar::deserialize(const EC_Group& group, std::span bytes) { if(auto v = group._data()->scalar_deserialize(bytes)) { - return EC_Scalar(group, std::move(v)); + return EC_Scalar(std::move(v)); } else { return {}; } } bool EC_Scalar::is_zero() const { - return group()->scalar_is_zero(inner()); + return inner().is_zero(); } EC_Scalar EC_Scalar::invert() const { - return EC_Scalar(group(), group()->scalar_invert(inner())); + return EC_Scalar(inner().invert()); } EC_Scalar EC_Scalar::negate() const { - return EC_Scalar(m_group, group()->scalar_negate(inner())); + return EC_Scalar(inner().negate()); } void EC_Scalar::square_self() { - group()->scalar_square_self(*m_scalar); + m_scalar->square_self(); } EC_Scalar EC_Scalar::add(const EC_Scalar& x) const { - BOTAN_ARG_CHECK(group() == x.group(), "Curve mismatch"); - return EC_Scalar(group(), group()->scalar_add(inner(), x.inner())); + return EC_Scalar(inner().add(x.inner())); } EC_Scalar EC_Scalar::sub(const EC_Scalar& x) const { - BOTAN_ARG_CHECK(group() == x.group(), "Curve mismatch"); - return EC_Scalar(group(), group()->scalar_sub(inner(), x.inner())); + return EC_Scalar(inner().sub(x.inner())); } EC_Scalar EC_Scalar::mul(const EC_Scalar& x) const { - BOTAN_ARG_CHECK(group() == x.group(), "Curve mismatch"); - return EC_Scalar(group(), group()->scalar_mul(inner(), x.inner())); + return EC_Scalar(inner().mul(x.inner())); } void EC_Scalar::assign(const EC_Scalar& x) { - BOTAN_ARG_CHECK(group() == x.group(), "Curve mismatch"); - group()->scalar_assign(*m_scalar, x.inner()); + m_scalar->assign(x.inner()); } bool EC_Scalar::is_eq(const EC_Scalar& x) const { - BOTAN_ARG_CHECK(group() == x.group(), "Curve mismatch"); - return group()->scalar_is_eq(inner(), x.inner()); + return inner().is_eq(x.inner()); } } // namespace Botan diff --git a/src/lib/pubkey/ec_group/ec_scalar.h b/src/lib/pubkey/ec_group/ec_scalar.h index 4065c28366d..fc8e21b426d 100644 --- a/src/lib/pubkey/ec_group/ec_scalar.h +++ b/src/lib/pubkey/ec_group/ec_scalar.h @@ -89,7 +89,10 @@ class BOTAN_UNSTABLE_API EC_Scalar final { */ static EC_Scalar gk_x_mod_order(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector& ws); - size_t bytes() const { return m_scalar_bytes; } + /** + * Return the byte size of this scalar + */ + size_t bytes() const; /** * Write the fixed length serialization to bytes @@ -188,28 +191,23 @@ class BOTAN_UNSTABLE_API EC_Scalar final { EC_Scalar(const EC_Scalar& other); EC_Scalar(EC_Scalar&& other) noexcept; - EC_Scalar& operator=(const EC_Scalar& x) { - this->assign(x); - return (*this); - } + EC_Scalar& operator=(const EC_Scalar& other); + EC_Scalar& operator=(EC_Scalar&& other) noexcept; ~EC_Scalar(); + const EC_Scalar_Data& _inner() const { return inner(); } + + static EC_Scalar _from_inner(std::unique_ptr inner); + private: friend class EC_AffinePoint; - friend class EC_Mul2Table_Data; - - EC_Scalar(std::shared_ptr group, std::unique_ptr scalar); - EC_Scalar(const EC_Group& group, std::unique_ptr scalar); + EC_Scalar(std::unique_ptr scalar); const EC_Scalar_Data& inner() const { return *m_scalar; } - const std::shared_ptr& group() const { return m_group; } - - std::shared_ptr m_group; std::unique_ptr m_scalar; - const size_t m_scalar_bytes; }; } // namespace Botan diff --git a/src/lib/pubkey/ec_group/info.txt b/src/lib/pubkey/ec_group/info.txt index a9dfea5717d..d918f52f054 100644 --- a/src/lib/pubkey/ec_group/info.txt +++ b/src/lib/pubkey/ec_group/info.txt @@ -16,6 +16,7 @@ pem point_mul.h +ec_inner_bn.h ec_inner_data.h diff --git a/src/lib/pubkey/ec_h2c/ec_h2c.cpp b/src/lib/pubkey/ec_h2c/ec_h2c.cpp index 0a7018eca25..e3a68a903ca 100644 --- a/src/lib/pubkey/ec_h2c/ec_h2c.cpp +++ b/src/lib/pubkey/ec_h2c/ec_h2c.cpp @@ -7,6 +7,7 @@ #include #include +#include #include namespace Botan { @@ -16,10 +17,18 @@ EC_Point hash_to_curve_sswu(const EC_Group& group, std::span input, std::span domain_sep, bool random_oracle) { - if(auto group_id = PCurve::PrimeOrderCurveId::from_oid(group.get_curve_oid())) { + return group.OS2ECP(hash_to_curve_sswu(*group._data(), hash_fn, input, domain_sep, random_oracle)); +} + +std::vector hash_to_curve_sswu(const EC_Group_Data& group, + std::string_view hash_fn, + std::span input, + std::span domain_sep, + bool random_oracle) { + if(auto group_id = PCurve::PrimeOrderCurveId::from_oid(group.oid())) { if(auto curve = PCurve::PrimeOrderCurve::from_id(*group_id)) { const auto pt = curve->hash_to_curve(hash_fn, input, domain_sep, random_oracle); - return group.OS2ECP(pt.to_affine().serialize()); + return pt.to_affine().serialize(); } } diff --git a/src/lib/pubkey/ec_h2c/ec_h2c.h b/src/lib/pubkey/ec_h2c/ec_h2c.h index bcdf1fc9d04..0d6a09c7010 100644 --- a/src/lib/pubkey/ec_h2c/ec_h2c.h +++ b/src/lib/pubkey/ec_h2c/ec_h2c.h @@ -15,6 +15,7 @@ namespace Botan { class EC_Group; +class EC_Group_Data; /** * Hash an input onto an elliptic curve point using the @@ -29,6 +30,19 @@ EC_Point hash_to_curve_sswu(const EC_Group& group, std::span domain_sep, bool random_oracle); +/** +* Hash an input onto an elliptic curve point using the +* methods from RFC 9380 +* +* This method requires that the ECC group have (a*b) != 0 +* which excludes certain groups including secp256k1 +*/ +std::vector hash_to_curve_sswu(const EC_Group_Data& group_data, + std::string_view hash_fn, + std::span input, + std::span domain_sep, + bool random_oracle); + } // namespace Botan #endif diff --git a/src/tests/data/pubkey/ecdsa_verify.vec b/src/tests/data/pubkey/ecdsa_verify.vec index 8bf5b572983..dd538ff9f68 100644 --- a/src/tests/data/pubkey/ecdsa_verify.vec +++ b/src/tests/data/pubkey/ecdsa_verify.vec @@ -69,14 +69,6 @@ Py = 81333916963110019576228330948951168219884247801435258672405011123948094 Msg = 0679246D6C4216DE0DAA08E5523FB2674DB2B6599C3B72FF946B488A15290B62 Signature = 30cf3ae9da8c18ef37664e358e43b07f93ded599653e64acd171e197a1c72f9ad521e5e2e091e9fe4c27f1110265ec5cbb701a6faf3569304774de5f -# From CryptoFuzz -Group = secp256k1 -Valid = 0 -Px = 70000000000000000 -Py = 0 -Msg = d1a0b2537473db2edf298bab7cb968882ab9d558c2a6298b4556ee9d36298cae -Signature = 79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798D1A0B2537473DB2EDF298BAB7CB968882AB9D558C2A6298B4556EE9D36298CAE - # OSS-Fuzz 32470 Group = secp256k1 Valid = 1