Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EC_AffinePoint API improvements #4446

Merged
merged 1 commit into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions doc/api_ref/ecc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,38 @@ Only curves over prime fields are supported.

Fixed base scalar multiplication. Constant time with blinding.

.. cpp:function:: static std::optional<EC_AffinePoint> mul_px_qy(const EC_AffinePoint& p, \
const EC_Scalar& x, \
const EC_AffinePoint& q, \
const EC_Scalar& y, \
RandomNumberGenerator& rng)

Constant time 2-ary multiscalar multiplication. Returns p*x + q*y, or
nullopt if the resulting point was the identity element.

.. cpp:function:: EC_AffinePoint mul(const EC_Scalar& scalar, RandomNumberGenerator& rng, std::vector<BigInt>& ws) const

Variable base scalar multiplication. Constant time with blinding.

.. cpp:function:: static EC_AffinePoint add(const EC_AffinePoint& p, const EC_AffinePoint& q)

Elliptic curve point addition.

.. note::

This point addition operation is relatively quite expensive since it
must convert the point directly from projective to affine coordinates,
which requires an expensive field inversion. This is, however,
sufficient for protocols which just require a small number of point
additions. In the future a type for projective coordinate points may
also be added, to better handle protocols which require many point
additions. If you are implementing such a protocol using this interface
please open an issue on Github.

.. cpp:function:: EC_AffinePoint negate() const

Return the negation of this point.

.. cpp:function:: static EC_AffinePoint hash_to_curve_ro(const EC_Group& group, \
std::string_view hash_fn, \
std::span<const uint8_t> input, \
Expand Down
4 changes: 4 additions & 0 deletions src/cli/perf_ec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class PerfTest_EllipticCurve final : public PerfTest {

auto bp_timer = config.make_timer(group_name + " base point mul");
auto vp_timer = config.make_timer(group_name + " variable point mul");
auto add_timer = config.make_timer(group_name + " point addition");
auto der_uc_timer = config.make_timer(group_name + " point deserialize (uncompressed)");
auto der_c_timer = config.make_timer(group_name + " point deserialize (compressed)");
auto mul2_setup_timer = config.make_timer(group_name + " mul2 setup");
Expand Down Expand Up @@ -64,6 +65,8 @@ class PerfTest_EllipticCurve final : public PerfTest {
const auto r2_bytes = r2.serialize_uncompressed();
BOTAN_ASSERT_EQUAL(r1_bytes, r2_bytes, "Same result for multiplication");

add_timer->run([&]() { r1.add(r2); });

der_uc_timer->run([&]() { Botan::EC_AffinePoint::deserialize(group, r1_bytes); });

const auto r1_cbytes = r1.serialize_compressed();
Expand All @@ -81,6 +84,7 @@ class PerfTest_EllipticCurve final : public PerfTest {
}
}

config.record_result(*add_timer);
config.record_result(*bp_timer);
config.record_result(*vp_timer);
config.record_result(*mul2_setup_timer);
Expand Down
9 changes: 6 additions & 3 deletions src/lib/math/pcurves/pcurves.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ class BOTAN_TEST_API PrimeOrderCurve {
return bytes;
}

/**
* Point negation
*/
AffinePoint negate() const { return m_curve->point_negate(*this); }

/**
* Return true if this is the curve identity element (aka the point at infinity)
*/
Expand Down Expand Up @@ -243,8 +248,6 @@ class BOTAN_TEST_API PrimeOrderCurve {

ProjectivePoint dbl() const { return m_curve->point_double(*this); }

ProjectivePoint negate() const { return m_curve->point_negate(*this); }

friend ProjectivePoint operator+(const ProjectivePoint& x, const ProjectivePoint& y) {
return x.m_curve->point_add(x, y);
}
Expand Down Expand Up @@ -376,7 +379,7 @@ class BOTAN_TEST_API PrimeOrderCurve {

virtual ProjectivePoint point_double(const ProjectivePoint& pt) const = 0;

virtual ProjectivePoint point_negate(const ProjectivePoint& pt) const = 0;
virtual AffinePoint point_negate(const AffinePoint& pt) const = 0;

virtual ProjectivePoint point_add(const ProjectivePoint& a, const ProjectivePoint& b) const = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/lib/math/pcurves/pcurves_impl/pcurves_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ class PrimeOrderCurveImpl final : public PrimeOrderCurve {
return stash(from_stash(a) + from_stash(b));
}

ProjectivePoint point_negate(const ProjectivePoint& pt) const override { return stash(from_stash(pt).negate()); }
AffinePoint point_negate(const AffinePoint& pt) const override { return stash(from_stash(pt).negate()); }

bool affine_point_is_identity(const AffinePoint& pt) const override {
return from_stash(pt).is_identity().as_bool();
Expand Down
24 changes: 19 additions & 5 deletions src/lib/pubkey/ec_group/ec_apoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,26 @@ EC_AffinePoint EC_AffinePoint::mul(const EC_Scalar& scalar, RandomNumberGenerato
return EC_AffinePoint(inner().mul(scalar._inner(), rng, ws));
}

EC_AffinePoint EC_AffinePoint::mul_px_qy(const EC_AffinePoint& p,
const EC_Scalar& x,
const EC_AffinePoint& q,
const EC_Scalar& y,
RandomNumberGenerator& rng) {
std::optional<EC_AffinePoint> EC_AffinePoint::mul_px_qy(const EC_AffinePoint& p,
const EC_Scalar& x,
const EC_AffinePoint& q,
const EC_Scalar& y,
RandomNumberGenerator& rng) {
auto pt = p._inner().group()->mul_px_qy(p._inner(), x._inner(), q._inner(), y._inner(), rng);
if(pt) {
return EC_AffinePoint(std::move(pt));
} else {
return {};
}
randombit marked this conversation as resolved.
Show resolved Hide resolved
}

EC_AffinePoint EC_AffinePoint::add(const EC_AffinePoint& q) const {
auto pt = _inner().group()->affine_add(_inner(), q._inner());
return EC_AffinePoint(std::move(pt));
}

EC_AffinePoint EC_AffinePoint::negate() const {
auto pt = this->_inner().group()->affine_neg(this->_inner());
return EC_AffinePoint(std::move(pt));
}

Expand Down
28 changes: 23 additions & 5 deletions src/lib/pubkey/ec_group/ec_apoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class EC_Point;
class EC_Group_Data;
class EC_AffinePoint_Data;

/// Elliptic Curve in Affine Representation
///
class BOTAN_UNSTABLE_API EC_AffinePoint final {
public:
/// Point deserialization. Throws if wrong length or not a valid point
Expand Down Expand Up @@ -79,11 +81,27 @@ class BOTAN_UNSTABLE_API EC_AffinePoint final {
/// Compute 2-ary multiscalar multiplication - p*x + q*y
///
/// This operation runs in constant time with respect to p, x, q, and y
static EC_AffinePoint mul_px_qy(const EC_AffinePoint& p,
const EC_Scalar& x,
const EC_AffinePoint& q,
const EC_Scalar& y,
RandomNumberGenerator& rng);
///
/// @returns p*x+q*y, or nullopt if the result was the point at infinity
static std::optional<EC_AffinePoint> mul_px_qy(const EC_AffinePoint& p,
const EC_Scalar& x,
const EC_AffinePoint& q,
const EC_Scalar& y,
RandomNumberGenerator& rng);

/// Point addition
///
/// Note that this is quite slow since it converts the resulting
/// projective point immediately to affine coordinates, which requires a
/// field inversion. This can be sufficient when implementing protocols
/// that just need to perform a few additions.
///
/// In the future a cooresponding EC_ProjectivePoint type may be added
/// which would avoid the expensive affine conversions
EC_AffinePoint add(const EC_AffinePoint& q) const;

/// Point negation
EC_AffinePoint negate() const;

/// Return the number of bytes of a field element
///
Expand Down
27 changes: 26 additions & 1 deletion src/lib/pubkey/ec_group/ec_inner_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,16 +317,41 @@ std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::mul_px_qy(const EC_AffinePoi
auto qy = q_mul.mul(EC_Scalar_Data_BN::checked_ref(y).value(), rng, order, ws);

auto px_qy = px + qy;
px_qy.force_affine();

if(!px_qy.is_zero()) {
px_qy.force_affine();
return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(px_qy));
} else {
return nullptr;
}
}
}

std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_add(const EC_AffinePoint_Data& p,
const EC_AffinePoint_Data& q) const {
if(m_pcurve) {
auto pt = m_pcurve->point_add_mixed(
PCurve::PrimeOrderCurve::ProjectivePoint::from_affine(EC_AffinePoint_Data_PC::checked_ref(p).value()),
EC_AffinePoint_Data_PC::checked_ref(q).value());

return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), pt.to_affine());
} else {
auto pt = p.to_legacy_point() + q.to_legacy_point();
return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
}
}

std::unique_ptr<EC_AffinePoint_Data> EC_Group_Data::affine_neg(const EC_AffinePoint_Data& p) const {
if(m_pcurve) {
auto pt = m_pcurve->point_negate(EC_AffinePoint_Data_PC::checked_ref(p).value());
return std::make_unique<EC_AffinePoint_Data_PC>(shared_from_this(), pt);
} else {
auto pt = p.to_legacy_point();
pt.negate(); // negates in place
return std::make_unique<EC_AffinePoint_Data_BN>(shared_from_this(), std::move(pt));
}
}

std::unique_ptr<EC_Mul2Table_Data> EC_Group_Data::make_mul2_table(const EC_AffinePoint_Data& h) const {
if(m_pcurve) {
EC_AffinePoint_Data_PC g(shared_from_this(), m_pcurve->generator());
Expand Down
4 changes: 4 additions & 0 deletions src/lib/pubkey/ec_group/ec_inner_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ class EC_Group_Data final : public std::enable_shared_from_this<EC_Group_Data> {
const EC_Scalar_Data& y,
RandomNumberGenerator& rng) const;

std::unique_ptr<EC_AffinePoint_Data> affine_add(const EC_AffinePoint_Data& p, const EC_AffinePoint_Data& q) const;

std::unique_ptr<EC_AffinePoint_Data> affine_neg(const EC_AffinePoint_Data& p) const;

std::unique_ptr<EC_Mul2Table_Data> make_mul2_table(const EC_AffinePoint_Data& pt) const;

const PCurve::PrimeOrderCurve& pcurve() const {
Expand Down
Loading
Loading