+
+#include "scl/coro/batch.h"
+#include "scl/coro/future.h"
+#include "scl/coro/promise.h"
+#include "scl/coro/sleep_awaiter.h"
+#include "scl/coro/task.h"
+#include "scl/util/time.h"
+
+namespace scl::coro {
+
+/**
+ * @brief Interface for a coroutine runtime.
+ *
+ * A coroutine runtime should be able to handle scheduling and descheduling
+ * of coroutines, as well as determination of which coroutines gets to run next.
+ *
+ *
Scheduled coroutines can roughly be divided into three categories:
+ * Coroutines that can be executed as soon as possible; coroutines which can be
+ * executed when some predicate becomes true, and coroutines that can be
+ * executed after some time has passed. This interface contains functions for
+ * each case, and instantiations handle each slightly differently.
+ */
+class Runtime {
+ public:
+ virtual ~Runtime() {}
+
+ /**
+ * @brief Schedule a coroutine for execution when some predicate is true.
+ * @param coroutine the coroutine.
+ * @param predicate a predicate indicating when the coroutine can be resumed.
+ */
+ virtual void schedule(std::coroutine_handle<> coroutine,
+ std::function&& predicate) = 0;
+
+ /**
+ * @brief Schedule a coroutine for execution after some delay.
+ * @param coroutine the coroutine.
+ * @param delay a delay.
+ */
+ virtual void schedule(std::coroutine_handle<> coroutine,
+ util::Time::Duration delay) = 0;
+
+ /**
+ * @brief Schedule a coroutine for execution immediately.
+ * @param coroutine the coroutine.
+ */
+ void schedule(std::coroutine_handle<> coroutine) {
+ return this->schedule(coroutine, []() { return true; });
+ }
+
+ /**
+ * @brief Deschedule a coroutine.
+ * @param coroutine the coroutine.
+ */
+ virtual void deschedule(std::coroutine_handle<> coroutine) = 0;
+
+ /**
+ * @brief Check if there are coroutines that still need to be executed.
+ */
+ virtual bool taskQueueEmpty() const = 0;
+
+ /**
+ * @brief Get the next coroutine to execute.
+ */
+ virtual std::coroutine_handle<> next() = 0;
+
+ /**
+ * @brief Assigns this runtime to an awaitable.
+ * @param awaitable the awaitable, a type with a "setRuntime" function.
+ */
+ template
+ void assignTo(AWAITABLE& awaitable) {
+ awaitable.setRuntime(this);
+ }
+
+ /**
+ * @brief Run a task to completion.
+ * @param task the task.
+ * @return the result of running \p task.
+ */
+ template
+ RESULT run(Task&& task) {
+ task.setRuntime(this);
+ schedule(task.m_handle);
+
+ while (!taskQueueEmpty()) {
+ next().resume();
+ }
+
+ if constexpr (std::is_void_v) {
+ task.result();
+ } else {
+ return task.result();
+ }
+ }
+};
+
+/**
+ * @brief A Default implementation for a coroutine runtime.
+ */
+class DefaultRuntime final : public Runtime {
+ using Pair = std::pair, std::function>;
+
+ public:
+ /**
+ * @brief Create a default runtime.
+ */
+ static std::unique_ptr create() {
+ return std::make_unique();
+ }
+
+ ~DefaultRuntime() {}
+
+ void schedule(std::coroutine_handle<> coroutine,
+ std::function&& predicate) override {
+ m_tq.emplace_back(coroutine, std::move(predicate));
+ }
+
+ void schedule(std::coroutine_handle<> coroutine,
+ util::Time::Duration delay) override {
+ const auto start = util::Time::now();
+ schedule(coroutine, [start, delay]() {
+ const auto now = util::Time::now();
+ return now - start >= delay;
+ });
+ }
+
+ void deschedule(std::coroutine_handle<> coroutine) override;
+
+ bool taskQueueEmpty() const override {
+ return m_tq.empty();
+ }
+
+ std::coroutine_handle<> next() override;
+
+ private:
+ std::list m_tq;
+};
+
+namespace details {
+
+template
+AWAITABLE TaskPromiseBase::await_transform(AWAITABLE&& awaitable) {
+ m_runtime->assignTo(awaitable);
+ return std::forward(awaitable);
+}
+
+template
+std::coroutine_handle<> FutureAwaiter::await_suspend(
+ std::coroutine_handle<> handle) {
+ m_runtime->schedule(handle, std::move(m_future));
+ return m_runtime->next();
+}
+
+template
+std::coroutine_handle<> Batch::await_suspend(
+ std::coroutine_handle<> coroutine) {
+ for (auto& task : m_tasks) {
+ task.setRuntime(m_runtime);
+ m_runtime->schedule(task.m_handle);
+ }
+
+ m_runtime->schedule(coroutine, [this]() { return await_ready(); });
+
+ return m_runtime->next();
+}
+
+template
+std::coroutine_handle<> PartialBatch::await_suspend(
+ std::coroutine_handle<> coroutine) {
+ for (auto& task : m_tasks) {
+ task.setRuntime(m_runtime);
+ m_runtime->schedule(task.m_handle);
+ }
+
+ m_runtime->schedule(coroutine, [this]() { return await_ready(); });
+
+ return m_runtime->next();
+}
+
+inline std::coroutine_handle<> SleepAwaiter::await_suspend(
+ std::coroutine_handle<> handle) {
+ m_runtime->schedule(handle, m_duration);
+ return m_runtime->next();
+}
+
+} // namespace details
+} // namespace scl::coro
+
+#endif // SCL_CORO_RUNTIME_H
diff --git a/include/scl/coro/sleep_awaiter.h b/include/scl/coro/sleep_awaiter.h
new file mode 100644
index 0000000..ab9f1c7
--- /dev/null
+++ b/include/scl/coro/sleep_awaiter.h
@@ -0,0 +1,78 @@
+/* SCL --- Secure Computation Library
+ * Copyright (C) 2024 Anders Dalskov
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef SCL_CORO_SLEEP_AWAITER_H
+#define SCL_CORO_SLEEP_AWAITER_H
+
+#include
+#include
+
+#include "scl/util/time.h"
+
+namespace scl::coro {
+
+class Runtime;
+
+namespace details {
+
+/**
+ * @brief Awaiter interface for suspending coroutines for some amount of time.
+ */
+class SleepAwaiter {
+ public:
+ /**
+ * @brief Create a new sleep awaiter.
+ */
+ SleepAwaiter(util::Time::Duration duration) : m_duration(duration) {}
+
+ /**
+ * @brief Check if the sleep awaiter is ready.
+ *
+ * The assumption made is that duration > 0
, and so this function
+ * always returns false.
+ */
+ bool await_ready() const noexcept {
+ return false;
+ }
+
+ /**
+ * @brief Suspend the coroutine that is being put to sleep.
+ */
+ std::coroutine_handle<> await_suspend(std::coroutine_handle<> handle);
+
+ /**
+ * @brief Resume the coroutine. Does nothing.
+ */
+ void await_resume() const noexcept {};
+
+ /**
+ * @brief Set the runtime for this coroutine.
+ */
+ void setRuntime(Runtime* runtime) {
+ m_runtime = runtime;
+ }
+
+ private:
+ util::Time::Duration m_duration;
+
+ Runtime* m_runtime = nullptr;
+};
+
+} // namespace details
+} // namespace scl::coro
+
+#endif // SCL_CORO_SLEEP_AWAITER_H
diff --git a/include/scl/coro/task.h b/include/scl/coro/task.h
new file mode 100644
index 0000000..eeedbac
--- /dev/null
+++ b/include/scl/coro/task.h
@@ -0,0 +1,193 @@
+/* SCL --- Secure Computation Library
+ * Copyright (C) 2024 Anders Dalskov
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef SCL_CORO_TASK_H
+#define SCL_CORO_TASK_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "scl/coro/batch.h"
+#include "scl/coro/promise.h"
+
+namespace scl::coro {
+
+class Runtime;
+
+namespace details {
+
+/**
+ * @brief Remove a handle from a runtime.
+ * @param runtime the runtime.
+ * @param handle handle for the coroutine.
+ *
+ * This function is used when Task destroys the coroutine state. Part of this
+ * teardown involves telling the current runtime that the handle should not be
+ * considered for resumption anymore.
+ */
+void removeHandle(Runtime* runtime, std::coroutine_handle<> handle);
+
+} // namespace details
+
+/**
+ * @brief A coroutine task.
+ * @tparam RESULT the type of the return value of the coroutine.
+ *
+ * coro::Task specifies a coroutine which returns a value of type \p RESULT.
+ * Tasks are cold start, i.e., they wont start executing until they are awaited.
+ *
+ * Tasks are move only types and considered the unique owner of the
+ * std::coroutine_handle which is associated with the coroutine.
+ */
+template
+class Task {
+ public:
+ /**
+ * @brief Promise type of Task.
+ */
+ using promise_type = details::TaskPromise;
+
+ /**
+ * @brief Destructor.
+ *
+ * The destructor of Task will first tell the current runtime to stop tracking
+ * the coroutine handle that this task manages. Afterwards the handle is
+ * destroyed.
+ */
+ ~Task() {
+ destroy();
+ }
+
+ /**
+ * @brief Move constructor.
+ */
+ Task(Task&& other) noexcept
+ : m_handle(std::exchange(other.m_handle, nullptr)) {}
+
+ /**
+ * @brief Construction from copy not allowed.
+ */
+ Task(const Task&) = delete;
+
+ /**
+ * @brief Move assignment.
+ */
+ Task& operator=(Task&& other) noexcept {
+ destroy();
+ m_handle = std::exchange(other.m_handle, nullptr);
+ return *this;
+ }
+
+ /**
+ * @brief Assignment from copy not allowed.
+ */
+ Task& operator=(const Task&) = delete;
+
+ /**
+ * @brief Allows a task to be co_await'ed.
+ */
+ auto operator co_await() {
+ struct Awaiter {
+ std::coroutine_handle coroutine;
+
+ // the awaiting corouting can resume immediately if the task it is waiting
+ // for already finished.
+ bool await_ready() noexcept {
+ return coroutine.done();
+ }
+
+ // the awaiting coroutine is suspended. So we will resume the task it is
+ // waiting for, and designate the awaiter as the coroutine that should be
+ // run when the task finishes.
+ std::coroutine_handle await_suspend(
+ std::coroutine_handle<> awaiter) {
+ coroutine.promise().setNext(awaiter);
+ return coroutine;
+ }
+
+ // get the result of the task being co_await'ed.
+ auto await_resume() {
+ return coroutine.promise().result();
+ }
+ };
+
+ return Awaiter{m_handle};
+ }
+
+ /**
+ * @brief Set the Runtime for executing this Task.
+ */
+ void setRuntime(Runtime* runtime) {
+ m_handle.promise().setRuntime(runtime);
+ }
+
+ /**
+ * @brief Destroy this task.
+ */
+ void destroy() {
+ if (m_handle) {
+ details::removeHandle(m_handle.promise().getRuntime(), m_handle);
+ m_handle.destroy();
+ }
+ }
+
+ /**
+ * @brief Check if a result is ready.
+ */
+ bool ready() const {
+ return m_handle.done();
+ }
+
+ /**
+ * @brief Get the return value of this task.
+ */
+ RESULT result() const {
+ return m_handle.promise().result();
+ }
+
+ /**
+ * @brief The coroutine handle associated with this task.
+ */
+ std::coroutine_handle m_handle;
+
+ private:
+ friend promise_type;
+
+ explicit Task(std::coroutine_handle handle)
+ : m_handle(handle) {}
+};
+
+namespace details {
+
+template
+Task TaskPromise::get_return_object() {
+ return Task(
+ std::coroutine_handle>::from_promise(*this));
+}
+
+inline Task TaskPromise::get_return_object() {
+ return Task(
+ std::coroutine_handle>::from_promise(*this));
+}
+
+} // namespace details
+} // namespace scl::coro
+
+#endif // SCL_CORO_TASK_H
diff --git a/include/scl/math/array.h b/include/scl/math/array.h
new file mode 100644
index 0000000..00dbbbe
--- /dev/null
+++ b/include/scl/math/array.h
@@ -0,0 +1,461 @@
+/* SCL --- Secure Computation Library
+ * Copyright (C) 2024 Anders Dalskov
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef SCL_MATH_ARRAY_H
+#define SCL_MATH_ARRAY_H
+
+#include
+#include
+#include
+#include
+
+#include "scl/serialization/serializer.h"
+#include "scl/util/prg.h"
+
+namespace scl {
+namespace math {
+
+/// @cond
+
+template
+concept PrefixIncrementable = requires(T a) { ++a; };
+
+template
+concept PrefixDecrementable = requires(T a) { --a; };
+
+template
+concept Multipliable = requires(T a, V b) { (a) * (b); };
+
+template
+concept Divisble = requires(T a, T b) { (a) / (b); };
+
+template
+struct MultiplyResultTypeTrait {
+ using Type = decltype(std::declval() * std::declval());
+};
+
+template
+using MultiplyResultType = typename MultiplyResultTypeTrait::Type;
+
+template
+concept Invertible = requires(T& a) { a.invert(); };
+
+/// @endcond
+
+/**
+ * @brief Array of values, e.g., group elements.
+ * @tparam T the array element type.
+ * @tparam N the number of elements.
+ *
+ * Array is effectively a wrapper around std::array
with
+ * added functionality that allows operating on Array objects as if they where
+ * group or ring elements. As such, Array behaves sort of like a direct product
+ * of N copies of the same group.
+ */
+template
+class Array final {
+ public:
+ /**
+ * @brief Binary size of this Array.
+ */
+ constexpr static std::size_t byteSize() {
+ return T::byteSize() * N;
+ }
+
+ /**
+ * @brief Read an array from a buffer.
+ */
+ static Array read(const unsigned char* src) {
+ Array p;
+ for (std::size_t i = 0; i < N; ++i) {
+ p.m_values[i] = T::read(src + i * T::byteSize());
+ }
+ return p;
+ } // LCOV_EXCL_LINE
+
+ /**
+ * @brief Create an array filled with random elements.
+ */
+ static Array random(util::PRG& prg)
+ requires requires() { T::random(prg); }
+ {
+ Array p;
+ for (std::size_t i = 0; i < N; ++i) {
+ p.m_values[i] = T::random(prg);
+ }
+ return p;
+ } // LCOV_EXCL_LINE
+
+ /**
+ * @brief Get an Array filled with the multiplicative identity
+ */
+ static Array one()
+ requires requires() { T::one(); }
+ {
+ return Array(T::one());
+ }
+
+ /**
+ * @brief Get an Array filled with the additive identity.
+ */
+ static Array zero() {
+ return Array(T::zero());
+ }
+
+ /**
+ * @brief Default constructor.
+ */
+ Array() {}
+
+ /**
+ * @brief Construct an Array filled with copies of the same element.
+ * @param element the element.
+ */
+ Array(const T& element) {
+ m_values.fill(element);
+ }
+
+ /**
+ * @brief Construct an Array filled with copies of the same element.
+ *
+ * This function will attempt to construct a \p T element using \p value, and
+ * then fill all slots with this value.
+ */
+ explicit Array(int value) : Array(T{value}){};
+
+ /**
+ * @brief Copy construct an Array from another array.
+ * @param arr the array.
+ */
+ Array(const std::array& arr) : m_values{arr} {}
+
+ /**
+ * @brief Move construct an Array from another array.
+ * @param arr the array.
+ */
+ Array(std::array&& arr) : m_values{std::move(arr)} {}
+
+ /**
+ * @brief Add another Array to this Array.
+ */
+ Array& operator+=(const Array& other) {
+ for (std::size_t i = 0; i < N; ++i) {
+ m_values[i] += other.m_values[i];
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Add two arrays.
+ */
+ friend Array operator+(const Array& lhs, const Array& rhs) {
+ Array tmp(lhs);
+ return tmp += rhs;
+ }
+
+ /**
+ * @brief Prefix increment operator.
+ */
+ Array& operator++()
+ requires PrefixIncrementable
+ {
+ for (auto& v : m_values) {
+ ++v;
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Postfix increment operator.
+ */
+ friend Array operator++(Array& arr, int)
+ requires PrefixIncrementable
+ {
+ Array tmp(arr);
+ for (auto& v : arr.m_values) {
+ ++v;
+ }
+ return tmp;
+ }
+
+ /**
+ * @brief Subtract an Array from this.
+ */
+ Array& operator-=(const Array& other) {
+ for (std::size_t i = 0; i < N; ++i) {
+ m_values[i] -= other.m_values[i];
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Subtract two Arrays.
+ */
+ friend Array operator-(const Array& lhs, const Array& rhs) {
+ Array tmp(lhs);
+ return tmp -= rhs;
+ }
+
+ /**
+ * @brief Prefix decrement operator.
+ */
+ Array& operator--()
+ requires PrefixDecrementable
+ {
+ for (auto& v : m_values) {
+ --v;
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Postfix decrement operator.
+ */
+ friend Array operator--(Array& arr, int)
+ requires PrefixDecrementable
+ {
+ Array tmp(arr);
+ for (auto& v : arr.m_values) {
+ --v;
+ }
+ return tmp;
+ }
+
+ /**
+ * @brief Negate this Array.
+ */
+ Array& negate() {
+ for (std::size_t i = 0; i < N; ++i) {
+ m_values[i].Negate();
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Multiply this Array with a scalar.
+ */
+ template
+ Array& operator*=(const S& scalar)
+ requires Multipliable
+ {
+ for (std::size_t i = 0; i < N; i++) {
+ m_values[i] *= scalar;
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Multiply two Arrays entry-wise.
+ */
+ template
+ Array& operator*=(const Array& other)
+ requires Multipliable
+ {
+ for (std::size_t i = 0; i < N; ++i) {
+ m_values[i] *= other[i];
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Multiply a scalar with this Array.
+ */
+ template
+ Array operator*(const S& scalar) const
+ requires Multipliable
+ {
+ auto copy = *this;
+ return copy *= scalar;
+ }
+
+ /**
+ * @brief Multiply two Arrays entry-wise.
+ */
+ template
+ friend Array, N> operator*(const Array& lhs,
+ const Array& rhs)
+ requires Multipliable
+ {
+ Array, N> tmp;
+ for (std::size_t i = 0; i < N; i++) {
+ tmp[i] = lhs[i] * rhs[i];
+ }
+ return tmp;
+ }
+
+ /**
+ * @brief Invert all entries in this Array.
+ */
+ Array& invert()
+ requires Invertible
+ {
+ for (std::size_t i = 0; i < N; ++i) {
+ m_values[i].invert();
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Compute the inverse of each entry in this Array.
+ */
+ Array Inverse() const
+ requires Invertible
+ {
+ Array p = *this;
+ return p.Invert();
+ }
+
+ /**
+ * @brief Divide this Array by another Array entry-wise.
+ */
+ Array operator/=(const Array& other)
+ requires Divisble
+ {
+ for (std::size_t i = 0; i < N; ++i) {
+ m_values[i] /= other[i];
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Divide this Array by another Array entry-wise.
+ */
+ Array operator/(const Array& other) const
+ requires Divisble
+ {
+ Array r = *this;
+ r /= other;
+ return r;
+ } // LCOV_EXCL_LINE
+
+ /**
+ * @brief Get the value at a particular entry in the product element.
+ */
+ T& operator[](std::size_t index) {
+ return m_values[index];
+ }
+
+ /**
+ * @brief Get the value at a particular entry in the product element.
+ */
+ T operator[](std::size_t index) const {
+ return m_values[index];
+ }
+
+ /**
+ * @brief Compare two product elements.
+ */
+ bool equal(const Array& other) const {
+ bool eq = true;
+ for (std::size_t i = 0; i < N; ++i) {
+ eq = eq && (m_values[i] == other.m_values[i]);
+ }
+ return eq;
+ }
+
+ /**
+ * @brief Equality operator for Array.
+ */
+ friend bool operator==(const Array& lhs, const Array& rhs) {
+ return lhs.equal(rhs);
+ }
+
+ /**
+ * @brief In-equality operator for Array.
+ */
+ friend bool operator!=(const Array& lhs, const Array& rhs) {
+ return !(lhs == rhs);
+ }
+
+ /**
+ * @brief Get a string representation of this product element.
+ */
+ std::string toString() const {
+ std::stringstream ss;
+ ss << "P{";
+ for (std::size_t i = 0; i < N - 1; ++i) {
+ ss << m_values[i] << ", ";
+ }
+ ss << m_values[N - 1] << "}";
+ return ss.str();
+ }
+
+ /**
+ * @brief Stream printing operator for Array.
+ */
+ friend std::ostream& operator<<(std::ostream& os, const Array& array) {
+ return os << array.toString();
+ }
+
+ /**
+ * @brief Write this product element to a buffer.
+ */
+ void write(unsigned char* dest) const {
+ for (std::size_t i = 0; i < N; ++i) {
+ m_values[i].write(dest + i * T::byteSize());
+ }
+ }
+
+ private:
+ std::array m_values;
+};
+
+} // namespace math
+
+namespace seri {
+
+/**
+ * @brief Serializer specialization for product types.
+ */
+template
+struct Serializer> {
+ /**
+ * @brief Get the serialized size of a product type.
+ */
+ static constexpr std::size_t sizeOf(
+ const math::Array& /* ignored */) {
+ return math::Array::byteSize();
+ }
+
+ /**
+ * @brief Write a product element to a buffer.
+ * @param prod the element.
+ * @param buf the buffer.
+ */
+ static std::size_t write(const math::Array& prod,
+ unsigned char* buf) {
+ prod.write(buf);
+ return sizeOf(prod);
+ }
+
+ /**
+ * @brief Read a product element from a buffer.
+ * @param prod the output.
+ * @param buf the buffer.
+ */
+ static std::size_t read(math::Array& prod,
+ const unsigned char* buf) {
+ prod = math::Array::read(buf);
+ return sizeOf(prod);
+ }
+};
+
+} // namespace seri
+
+} // namespace scl
+
+#endif // SCL_MATH_ARRAY_H
diff --git a/include/scl/math/ec_ops.h b/include/scl/math/curves/ec_ops.h
similarity index 60%
rename from include/scl/math/ec_ops.h
rename to include/scl/math/curves/ec_ops.h
index 6adabb0..d96bfaf 100644
--- a/include/scl/math/ec_ops.h
+++ b/include/scl/math/curves/ec_ops.h
@@ -1,5 +1,5 @@
/* SCL --- Secure Computation Library
- * Copyright (C) 2023 Anders Dalskov
+ * Copyright (C) 2024 Anders Dalskov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -15,35 +15,38 @@
* along with this program. If not, see .
*/
-#ifndef SCL_MATH_EC_OPS_H
-#define SCL_MATH_EC_OPS_H
+#ifndef SCL_MATH_CURVES_EC_OPS_H
+#define SCL_MATH_CURVES_EC_OPS_H
+
+#include
+#include
#include "scl/math/ff.h"
#include "scl/math/number.h"
-namespace scl::math {
+namespace scl::math::ec {
/**
* @brief Set a point equal to the point-at-infinity.
* @param out the point to set to the point-at-infinity.
*/
-template
-void CurveSetPointAtInfinity(typename C::ValueType& out);
+template
+void setPointAtInfinity(typename CURVE::ValueType& out);
/**
* @brief Check if a point is equal to the point-at-infinity.
* @param point the point
* @return true if \p point is equal to the point-at-infinity, otherwise false.
*/
-template
-bool CurveIsPointAtInfinity(const typename C::ValueType& point);
+template
+bool isPointAtInfinity(const typename CURVE::ValueType& point);
/**
* @brief Set a point equal to the generator of this curve.
* @param out the point to set equal to the generator of this curve.
*/
-template
-void CurveSetGenerator(typename C::ValueType& out);
+template
+void setGenerator(typename CURVE::ValueType& out);
/**
* @brief Set a point equal to an affine point.
@@ -51,83 +54,84 @@ void CurveSetGenerator(typename C::ValueType& out);
* @param x the x coordinate
* @param y the y coordinate
*/
-template
-void CurveSetAffine(typename C::ValueType& out,
- const FF& x,
- const FF& y);
+template
+void setAffine(typename CURVE::ValueType& out,
+ const FF& x,
+ const FF& y);
/**
* @brief Convert a point to a pair of affine coordinates.
* @param point the point to convert.
* @return a set of affine coordinates.
*/
-template
-std::array, 2> CurveToAffine(
- const typename C::ValueType& point);
+template
+std::array, 2> toAffine(
+ const typename CURVE::ValueType& point);
/**
* @brief Add two elliptic curve points in-place.
* @param out the first point and output
* @param in the second point
*/
-template
-void CurveAdd(typename C::ValueType& out, const typename C::ValueType& in);
+template
+void add(typename CURVE::ValueType& out, const typename CURVE::ValueType& in);
/**
* @brief Double an elliptic curve point in-place.
* @param out the point to double
*/
-template
-void CurveDouble(typename C::ValueType& out);
+template
+void dbl(typename CURVE::ValueType& out);
/**
* @brief Subtract two elliptic curve points in-place.
* @param out the first point and output
* @param in the second point
*/
-template
-void CurveSubtract(typename C::ValueType& out, const typename C::ValueType& in);
+template
+void subtract(typename CURVE::ValueType& out,
+ const typename CURVE::ValueType& in);
/**
* @brief Negate an elliptic curve point.
* @param out the point to negate
*/
-template
-void CurveNegate(typename C::ValueType& out);
+template
+void negate(typename CURVE::ValueType& out);
/**
* @brief Scalar multiply an elliptic curve point in-place.
* @param out the point
* @param scalar the scalar
*/
-template
-void CurveScalarMultiply(typename C::ValueType& out, const Number& scalar);
+template
+void scalarMultiply(typename CURVE::ValueType& out, const Number& scalar);
/**
* @brief Scalar multiply an elliptic curve point in-place.
* @param out the point
* @param scalar the scalar
*/
-template
-void CurveScalarMultiply(typename C::ValueType& out,
- const FF& scalar);
+template
+void scalarMultiply(typename CURVE::ValueType& out,
+ const FF& scalar);
/**
* @brief Check if two elliptic curve points are equal.
* @param in1 the first point
* @param in2 the second point
*/
-template
-bool CurveEqual(const typename C::ValueType& in1,
- const typename C::ValueType& in2);
+template
+bool equal(const typename CURVE::ValueType& in1,
+ const typename CURVE::ValueType& in2);
/**
* @brief Read an elliptic curve from a byte buffer.
* @param out where to store the point
* @param src the buffer
*/
-template
-void CurveFromBytes(typename C::ValueType& out, const unsigned char* src);
+template
+void fromBytes(typename CURVE::ValueType& out, const unsigned char* src);
/**
* @brief Write an elliptic curve point to a buffer.
@@ -135,19 +139,19 @@ void CurveFromBytes(typename C::ValueType& out, const unsigned char* src);
* @param in the elliptic curve point to write
* @param compress whether to compress the point
*/
-template
-void CurveToBytes(unsigned char* dest,
- const typename C::ValueType& in,
- bool compress);
+template
+void toBytes(unsigned char* dest,
+ const typename CURVE::ValueType& in,
+ bool compress);
/**
* @brief Convert an elliptic curve point to a string
* @param point the point
* @return an STL string representation of \p in.
*/
-template
-std::string CurveToString(const typename C::ValueType& point);
+template
+std::string toString(const typename CURVE::ValueType& point);
-} // namespace scl::math
+} // namespace scl::math::ec
-#endif // SCL_MATH_EC_OPS_H
+#endif // SCL_MATH_CURVES_EC_OPS_H
diff --git a/include/scl/math/curves/secp256k1.h b/include/scl/math/curves/secp256k1.h
index 085c944..ed97335 100644
--- a/include/scl/math/curves/secp256k1.h
+++ b/include/scl/math/curves/secp256k1.h
@@ -1,5 +1,5 @@
/* SCL --- Secure Computation Library
- * Copyright (C) 2023 Anders Dalskov
+ * Copyright (C) 2024 Anders Dalskov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -20,64 +20,28 @@
#include
-#include "scl/math/ec.h"
+#include "scl/math/curves/ec_ops.h"
#include "scl/math/ff.h"
+#include "scl/math/fields/secp256k1_field.h"
+#include "scl/math/fields/secp256k1_scalar.h"
-namespace scl::math {
+namespace scl::math::ec {
/**
* @brief Elliptic curve definition for secp256k1.
+ * @see http://www.secg.org/sec2-v2.pdf
*/
struct Secp256k1 {
/**
- * @brief The Field over which secp256k1 is defined.
+ * @brief The finite field defined by
+ * \f$p=2^{256}-2^{32}-2^{9}-2^{8}-2^{7}-2^{6}-2^{4}-1\f$
*/
- struct Field {
- /**
- * @brief Field elements are stored as 4 limb numbers internally.
- */
- using ValueType = std::array;
-
- /**
- * @brief Name of the secp256k1 field.
- */
- constexpr static const char* NAME = "secp256k1_field";
-
- /**
- * @brief Byte size of a secp256k1 field element.
- */
- constexpr static const std::size_t BYTE_SIZE = 4 * sizeof(mp_limb_t);
-
- /**
- * @brief Bit size of a secp256k1 field element.
- */
- constexpr static const std::size_t BIT_SIZE = 8 * BYTE_SIZE;
- };
+ using Field = ff::Secp256k1Field;
/**
- * @brief Finite field modulo a Secp256k1 prime order sub-group.
+ * @brief The finite field defined by a large prime order subgroup.
*/
- struct Scalar {
- /**
- * @brief Internal type of elements.
- */
- using ValueType = std::array;
-
- /**
- * @brief Name of the field.
- */
- constexpr static const char* NAME = "secp256k1_order";
-
- /**
- * @brief Size of an element in bytes.
- */
- constexpr static const std::size_t BYTE_SIZE = 4 * sizeof(mp_limb_t);
-
- /**
- * @brief Size of an element in bits.
- */
- constexpr static const std::size_t BIT_SIZE = 8 * BYTE_SIZE;
- };
+ using Scalar = ff::Secp256k1Scalar;
/**
* @brief Secp256k1 curve elements are stored in projective coordinates.
@@ -90,6 +54,6 @@ struct Secp256k1 {
constexpr static const char* NAME = "secp256k1";
};
-} // namespace scl::math
+} // namespace scl::math::ec
#endif // SCL_MATH_CURVES_SECP256K1_H
diff --git a/include/scl/math/ec.h b/include/scl/math/ec.h
index 9306872..ade2134 100644
--- a/include/scl/math/ec.h
+++ b/include/scl/math/ec.h
@@ -1,5 +1,5 @@
/* SCL --- Secure Computation Library
- * Copyright (C) 2023 Anders Dalskov
+ * Copyright (C) 2024 Anders Dalskov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -18,93 +18,100 @@
#ifndef SCL_MATH_EC_H
#define SCL_MATH_EC_H
+#include
+#include
#include
+#include
-#include "scl/math/ec_ops.h"
+#include "scl/math/array.h"
+#include "scl/math/curves/ec_ops.h"
#include "scl/math/ff.h"
#include "scl/math/number.h"
-#include "scl/math/ops.h"
-namespace scl::math {
+namespace scl {
+namespace math {
/**
* @brief Elliptic Curve interface.
- * @tparam Curve elliptic curve definition
+ * @tparam CURVE elliptic curve definition
*
- * TODO.
- *
- * @see FF
- * @see Secp256k1
+ * EC defines a point \f$P\f$ on some Elliptic Curve \f$E(K)\f$. The
+ * curve parameters is defined through the \p CURVE template parameter and
+ * appropriate overloads of the functions in the \ref ec namespace.
*/
-template
-class EC final : Add>, Eq>, Print> {
+template
+class EC final {
public:
/**
- * @brief The field that this curve is defined over.
+ * @brief Field that this curve is defined over.
*/
- using Field = FF;
+ using Field = FF;
/**
- * @brief A large sub-group of this curve.
+ * @brief Large subgroup of this curve.
*/
- using ScalarField = FF;
+ using ScalarField = FF;
/**
- * @brief The size of a curve point in bytes.
- * @param compressed
+ * @brief Size of a curve point in bytes.
*/
- constexpr static std::size_t ByteSize(bool compressed = true) {
- return 1 + (compressed ? 0 : Field::ByteSize()) + Field::ByteSize();
+ constexpr static std::size_t byteSize(bool compressed) {
+ return 1 + (compressed ? 0 : Field::byteSize()) + Field::byteSize();
}
/**
- * @brief The size of a curve point in bits.
+ * @brief Size of a curve point in bits.
*/
- constexpr static std::size_t BitSize(bool compressed = true) {
- return ByteSize(compressed) * 8;
+ constexpr static std::size_t bitSize(bool compressed) {
+ return byteSize(compressed) * 8;
}
/**
- * @brief A string indicating which curve this is.
+ * @brief String indicating which curve this is.
*/
- constexpr static const char* Name() {
- return Curve::NAME;
+ constexpr static const char* name() {
+ return CURVE::NAME;
}
/**
- * @brief Get the generator of this curve.
+ * @brief A generator of this curve.
*/
- constexpr static EC Generator() {
+ constexpr static EC generator() {
EC g;
- CurveSetGenerator(g.m_value);
+ ec::setGenerator(g.m_value);
return g;
- } // LCOV_EXCL_LINE
+ }
/**
- * @brief Read an elliptic curve point from bytes.
- * @param src the bytes
- * @return an elliptic curve point.
+ * @brief Reads an elliptic curve point from bytes.
*/
- static EC Read(const unsigned char* src) {
+ static EC read(const unsigned char* src) {
EC e;
- CurveFromBytes(e.m_value, src);
+ ec::fromBytes(e.m_value, src);
return e;
- } // LCOV_EXCL_LINE
+ }
/**
- * @brief Create a point from an pair of affine coordinates.
+ * @brief Creates a point from a pair of affine coordinates.
*/
- static EC FromAffine(const Field& x, const Field& y) {
+ static EC fromAffine(const Field& x, const Field& y) {
EC e;
- CurveSetAffine(e.m_value, x, y);
+ ec::setAffine(e.m_value, x, y);
return e;
}
+ /**
+ * @brief Get the additive identity of this curve.
+ */
+ static EC zero() {
+ return EC{};
+ }
+
/**
* @brief Create a new point equal to the point at infinity.
*/
- explicit constexpr EC() {
- CurveSetPointAtInfinity(m_value);
+ EC() {
+ ec::setPointAtInfinity(m_value);
}
/**
@@ -114,67 +121,70 @@ class EC final : Add>, Eq>, Print> {
/**
* @brief Add another EC point to this.
- * @param other the other point
- * @return this
*/
EC& operator+=(const EC& other) {
- CurveAdd(m_value, other.m_value);
+ ec::add(m_value, other.m_value);
return *this;
}
+ /**
+ * @brief Add two curve points.
+ */
+ friend EC operator+(const EC& lhs, const EC& rhs) {
+ EC tmp(lhs);
+ return tmp += rhs;
+ }
+
/**
* @brief Double this point.
- * @return this after doubling.
*/
- EC& DoubleInPlace() {
- CurveDouble(m_value);
+ EC& doublePointInPlace() {
+ ec::dbl(m_value);
return *this;
}
/**
* @brief Double this point.
- * @return \p this + \p this.
*/
- EC Double() const {
+ EC doublePoint() const {
EC copy(*this);
- return copy.DoubleInPlace();
+ return copy.doublePointInPlace();
}
/**
* @brief Subtract another point from this.
- * @param other the other point
- * @return this.
*/
EC& operator-=(const EC& other) {
- CurveSubtract(m_value, other.m_value);
+ ec::subtract(m_value, other.m_value);
return *this;
}
+ /**
+ * @brief Subtract two curve points.
+ */
+ friend EC operator-(const EC& lhs, const EC& rhs) {
+ EC tmp(lhs);
+ return tmp -= rhs;
+ }
+
/**
* @brief Perform a scalar multiplication.
- * @param scalar the scalar
- * @return this.
*/
EC& operator*=(const Number& scalar) {
- CurveScalarMultiply(m_value, scalar);
+ ec::scalarMultiply(m_value, scalar);
return *this;
}
/**
* @brief Perform a scalar multiplication.
- * @param scalar the scalar
- * @return this.
*/
EC& operator*=(const ScalarField& scalar) {
- CurveScalarMultiply(m_value, scalar);
+ ec::scalarMultiply(m_value, scalar);
return *this;
}
/**
* @brief Multiply a point with a scalar from the right.
- * @param point the point
- * @param scalar the scalar
- * @return the point multiplied with the scalar.
*/
friend EC operator*(const EC& point, const Number& scalar) {
EC copy(point);
@@ -183,9 +193,6 @@ class EC final : Add>, Eq