summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp')
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp731
1 files changed, 731 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp b/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp
new file mode 100644
index 0000000000..6c2302bd66
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp
@@ -0,0 +1,731 @@
+/*
+* Point arithmetic on elliptic curves over GF(p)
+*
+* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
+* 2008-2011,2012,2014,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/point_gfp.h>
+#include <botan/numthry.h>
+#include <botan/rng.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+PointGFp::PointGFp(const CurveGFp& curve) :
+ m_curve(curve),
+ m_coord_x(0),
+ m_coord_y(curve.get_1_rep()),
+ m_coord_z(0)
+ {
+ // Assumes Montgomery rep of zero is zero
+ }
+
+PointGFp::PointGFp(const CurveGFp& curve, const BigInt& x, const BigInt& y) :
+ m_curve(curve),
+ m_coord_x(x),
+ m_coord_y(y),
+ m_coord_z(m_curve.get_1_rep())
+ {
+ if(x < 0 || x >= curve.get_p())
+ throw Invalid_Argument("Invalid PointGFp affine x");
+ if(y < 0 || y >= curve.get_p())
+ throw Invalid_Argument("Invalid PointGFp affine y");
+
+ secure_vector<word> monty_ws(m_curve.get_ws_size());
+ m_curve.to_rep(m_coord_x, monty_ws);
+ m_curve.to_rep(m_coord_y, monty_ws);
+ }
+
+void PointGFp::randomize_repr(RandomNumberGenerator& rng)
+ {
+ secure_vector<word> ws(m_curve.get_ws_size());
+ randomize_repr(rng, ws);
+ }
+
+void PointGFp::randomize_repr(RandomNumberGenerator& rng, secure_vector<word>& ws)
+ {
+ const BigInt mask = BigInt::random_integer(rng, 2, m_curve.get_p());
+
+ /*
+ * No reason to convert this to Montgomery representation first,
+ * just pretend the random mask was chosen as Redc(mask) and the
+ * random mask we generated above is in the Montgomery
+ * representation.
+ * //m_curve.to_rep(mask, ws);
+ */
+ const BigInt mask2 = m_curve.sqr_to_tmp(mask, ws);
+ const BigInt mask3 = m_curve.mul_to_tmp(mask2, mask, ws);
+
+ m_coord_x = m_curve.mul_to_tmp(m_coord_x, mask2, ws);
+ m_coord_y = m_curve.mul_to_tmp(m_coord_y, mask3, ws);
+ m_coord_z = m_curve.mul_to_tmp(m_coord_z, mask, ws);
+ }
+
+namespace {
+
+inline void resize_ws(std::vector<BigInt>& ws_bn, size_t cap_size)
+ {
+ BOTAN_ASSERT(ws_bn.size() >= PointGFp::WORKSPACE_SIZE,
+ "Expected size for PointGFp workspace");
+
+ for(size_t i = 0; i != ws_bn.size(); ++i)
+ if(ws_bn[i].size() < cap_size)
+ ws_bn[i].get_word_vector().resize(cap_size);
+ }
+
+inline word all_zeros(const word x[], size_t len)
+ {
+ word z = 0;
+ for(size_t i = 0; i != len; ++i)
+ z |= x[i];
+ return CT::Mask<word>::is_zero(z).value();
+ }
+
+}
+
+void PointGFp::add_affine(const word x_words[], size_t x_size,
+ const word y_words[], size_t y_size,
+ std::vector<BigInt>& ws_bn)
+ {
+ if(all_zeros(x_words, x_size) & all_zeros(y_words, y_size))
+ {
+ return;
+ }
+
+ if(is_zero())
+ {
+ m_coord_x.set_words(x_words, x_size);
+ m_coord_y.set_words(y_words, y_size);
+ m_coord_z = m_curve.get_1_rep();
+ return;
+ }
+
+ resize_ws(ws_bn, m_curve.get_ws_size());
+
+ secure_vector<word>& ws = ws_bn[0].get_word_vector();
+ secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+ BigInt& T0 = ws_bn[2];
+ BigInt& T1 = ws_bn[3];
+ BigInt& T2 = ws_bn[4];
+ BigInt& T3 = ws_bn[5];
+ BigInt& T4 = ws_bn[6];
+
+ /*
+ https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
+ simplified with Z2 = 1
+ */
+
+ const BigInt& p = m_curve.get_p();
+
+ m_curve.sqr(T3, m_coord_z, ws); // z1^2
+ m_curve.mul(T4, x_words, x_size, T3, ws); // x2*z1^2
+
+ m_curve.mul(T2, m_coord_z, T3, ws); // z1^3
+ m_curve.mul(T0, y_words, y_size, T2, ws); // y2*z1^3
+
+ T4.mod_sub(m_coord_x, p, sub_ws); // x2*z1^2 - x1*z2^2
+
+ T0.mod_sub(m_coord_y, p, sub_ws);
+
+ if(T4.is_zero())
+ {
+ if(T0.is_zero())
+ {
+ mult2(ws_bn);
+ return;
+ }
+
+ // setting to zero:
+ m_coord_x.clear();
+ m_coord_y = m_curve.get_1_rep();
+ m_coord_z.clear();
+ return;
+ }
+
+ m_curve.sqr(T2, T4, ws);
+
+ m_curve.mul(T3, m_coord_x, T2, ws);
+
+ m_curve.mul(T1, T2, T4, ws);
+
+ m_curve.sqr(m_coord_x, T0, ws);
+ m_coord_x.mod_sub(T1, p, sub_ws);
+
+ m_coord_x.mod_sub(T3, p, sub_ws);
+ m_coord_x.mod_sub(T3, p, sub_ws);
+
+ T3.mod_sub(m_coord_x, p, sub_ws);
+
+ m_curve.mul(T2, T0, T3, ws);
+ m_curve.mul(T0, m_coord_y, T1, ws);
+ T2.mod_sub(T0, p, sub_ws);
+ m_coord_y.swap(T2);
+
+ m_curve.mul(T0, m_coord_z, T4, ws);
+ m_coord_z.swap(T0);
+ }
+
+void PointGFp::add(const word x_words[], size_t x_size,
+ const word y_words[], size_t y_size,
+ const word z_words[], size_t z_size,
+ std::vector<BigInt>& ws_bn)
+ {
+ if(all_zeros(x_words, x_size) & all_zeros(z_words, z_size))
+ return;
+
+ if(is_zero())
+ {
+ m_coord_x.set_words(x_words, x_size);
+ m_coord_y.set_words(y_words, y_size);
+ m_coord_z.set_words(z_words, z_size);
+ return;
+ }
+
+ resize_ws(ws_bn, m_curve.get_ws_size());
+
+ secure_vector<word>& ws = ws_bn[0].get_word_vector();
+ secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+ BigInt& T0 = ws_bn[2];
+ BigInt& T1 = ws_bn[3];
+ BigInt& T2 = ws_bn[4];
+ BigInt& T3 = ws_bn[5];
+ BigInt& T4 = ws_bn[6];
+ BigInt& T5 = ws_bn[7];
+
+ /*
+ https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
+ */
+
+ const BigInt& p = m_curve.get_p();
+
+ m_curve.sqr(T0, z_words, z_size, ws); // z2^2
+ m_curve.mul(T1, m_coord_x, T0, ws); // x1*z2^2
+ m_curve.mul(T3, z_words, z_size, T0, ws); // z2^3
+ m_curve.mul(T2, m_coord_y, T3, ws); // y1*z2^3
+
+ m_curve.sqr(T3, m_coord_z, ws); // z1^2
+ m_curve.mul(T4, x_words, x_size, T3, ws); // x2*z1^2
+
+ m_curve.mul(T5, m_coord_z, T3, ws); // z1^3
+ m_curve.mul(T0, y_words, y_size, T5, ws); // y2*z1^3
+
+ T4.mod_sub(T1, p, sub_ws); // x2*z1^2 - x1*z2^2
+
+ T0.mod_sub(T2, p, sub_ws);
+
+ if(T4.is_zero())
+ {
+ if(T0.is_zero())
+ {
+ mult2(ws_bn);
+ return;
+ }
+
+ // setting to zero:
+ m_coord_x.clear();
+ m_coord_y = m_curve.get_1_rep();
+ m_coord_z.clear();
+ return;
+ }
+
+ m_curve.sqr(T5, T4, ws);
+
+ m_curve.mul(T3, T1, T5, ws);
+
+ m_curve.mul(T1, T5, T4, ws);
+
+ m_curve.sqr(m_coord_x, T0, ws);
+ m_coord_x.mod_sub(T1, p, sub_ws);
+ m_coord_x.mod_sub(T3, p, sub_ws);
+ m_coord_x.mod_sub(T3, p, sub_ws);
+
+ T3.mod_sub(m_coord_x, p, sub_ws);
+
+ m_curve.mul(m_coord_y, T0, T3, ws);
+ m_curve.mul(T3, T2, T1, ws);
+
+ m_coord_y.mod_sub(T3, p, sub_ws);
+
+ m_curve.mul(T3, z_words, z_size, m_coord_z, ws);
+ m_curve.mul(m_coord_z, T3, T4, ws);
+ }
+
+void PointGFp::mult2i(size_t iterations, std::vector<BigInt>& ws_bn)
+ {
+ if(iterations == 0)
+ return;
+
+ if(m_coord_y.is_zero())
+ {
+ *this = PointGFp(m_curve); // setting myself to zero
+ return;
+ }
+
+ /*
+ TODO we can save 2 squarings per iteration by computing
+ a*Z^4 using values cached from previous iteration
+ */
+ for(size_t i = 0; i != iterations; ++i)
+ mult2(ws_bn);
+ }
+
+// *this *= 2
+void PointGFp::mult2(std::vector<BigInt>& ws_bn)
+ {
+ if(is_zero())
+ return;
+
+ if(m_coord_y.is_zero())
+ {
+ *this = PointGFp(m_curve); // setting myself to zero
+ return;
+ }
+
+ resize_ws(ws_bn, m_curve.get_ws_size());
+
+ secure_vector<word>& ws = ws_bn[0].get_word_vector();
+ secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+ BigInt& T0 = ws_bn[2];
+ BigInt& T1 = ws_bn[3];
+ BigInt& T2 = ws_bn[4];
+ BigInt& T3 = ws_bn[5];
+ BigInt& T4 = ws_bn[6];
+
+ /*
+ https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc
+ */
+ const BigInt& p = m_curve.get_p();
+
+ m_curve.sqr(T0, m_coord_y, ws);
+
+ m_curve.mul(T1, m_coord_x, T0, ws);
+ T1.mod_mul(4, p, sub_ws);
+
+ if(m_curve.a_is_zero())
+ {
+ // if a == 0 then 3*x^2 + a*z^4 is just 3*x^2
+ m_curve.sqr(T4, m_coord_x, ws); // x^2
+ T4.mod_mul(3, p, sub_ws); // 3*x^2
+ }
+ else if(m_curve.a_is_minus_3())
+ {
+ /*
+ if a == -3 then
+ 3*x^2 + a*z^4 == 3*x^2 - 3*z^4 == 3*(x^2-z^4) == 3*(x-z^2)*(x+z^2)
+ */
+ m_curve.sqr(T3, m_coord_z, ws); // z^2
+
+ // (x-z^2)
+ T2 = m_coord_x;
+ T2.mod_sub(T3, p, sub_ws);
+
+ // (x+z^2)
+ T3.mod_add(m_coord_x, p, sub_ws);
+
+ m_curve.mul(T4, T2, T3, ws); // (x-z^2)*(x+z^2)
+
+ T4.mod_mul(3, p, sub_ws); // 3*(x-z^2)*(x+z^2)
+ }
+ else
+ {
+ m_curve.sqr(T3, m_coord_z, ws); // z^2
+ m_curve.sqr(T4, T3, ws); // z^4
+ m_curve.mul(T3, m_curve.get_a_rep(), T4, ws); // a*z^4
+
+ m_curve.sqr(T4, m_coord_x, ws); // x^2
+ T4.mod_mul(3, p, sub_ws);
+ T4.mod_add(T3, p, sub_ws); // 3*x^2 + a*z^4
+ }
+
+ m_curve.sqr(T2, T4, ws);
+ T2.mod_sub(T1, p, sub_ws);
+ T2.mod_sub(T1, p, sub_ws);
+
+ m_curve.sqr(T3, T0, ws);
+ T3.mod_mul(8, p, sub_ws);
+
+ T1.mod_sub(T2, p, sub_ws);
+
+ m_curve.mul(T0, T4, T1, ws);
+ T0.mod_sub(T3, p, sub_ws);
+
+ m_coord_x.swap(T2);
+
+ m_curve.mul(T2, m_coord_y, m_coord_z, ws);
+ T2.mod_mul(2, p, sub_ws);
+
+ m_coord_y.swap(T0);
+ m_coord_z.swap(T2);
+ }
+
+// arithmetic operators
+PointGFp& PointGFp::operator+=(const PointGFp& rhs)
+ {
+ std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+ add(rhs, ws);
+ return *this;
+ }
+
+PointGFp& PointGFp::operator-=(const PointGFp& rhs)
+ {
+ PointGFp minus_rhs = PointGFp(rhs).negate();
+
+ if(is_zero())
+ *this = minus_rhs;
+ else
+ *this += minus_rhs;
+
+ return *this;
+ }
+
+PointGFp& PointGFp::operator*=(const BigInt& scalar)
+ {
+ *this = scalar * *this;
+ return *this;
+ }
+
+PointGFp operator*(const BigInt& scalar, const PointGFp& point)
+ {
+ BOTAN_DEBUG_ASSERT(point.on_the_curve());
+
+ const size_t scalar_bits = scalar.bits();
+
+ std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+ PointGFp R[2] = { point.zero(), point };
+
+ for(size_t i = scalar_bits; i > 0; i--)
+ {
+ const size_t b = scalar.get_bit(i - 1);
+ R[b ^ 1].add(R[b], ws);
+ R[b].mult2(ws);
+ }
+
+ if(scalar.is_negative())
+ R[0].negate();
+
+ BOTAN_DEBUG_ASSERT(R[0].on_the_curve());
+
+ return R[0];
+ }
+
+//static
+void PointGFp::force_all_affine(std::vector<PointGFp>& points,
+ secure_vector<word>& ws)
+ {
+ if(points.size() <= 1)
+ {
+ for(size_t i = 0; i != points.size(); ++i)
+ points[i].force_affine();
+ return;
+ }
+
+ for(size_t i = 0; i != points.size(); ++i)
+ {
+ if(points[i].is_zero())
+ throw Invalid_State("Cannot convert zero ECC point to affine");
+ }
+
+ /*
+ For >= 2 points use Montgomery's trick
+
+ See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography"
+ (Hankerson, Menezes, Vanstone)
+
+ TODO is it really necessary to save all k points in c?
+ */
+
+ const CurveGFp& curve = points[0].m_curve;
+ const BigInt& rep_1 = curve.get_1_rep();
+
+ if(ws.size() < curve.get_ws_size())
+ ws.resize(curve.get_ws_size());
+
+ std::vector<BigInt> c(points.size());
+ c[0] = points[0].m_coord_z;
+
+ for(size_t i = 1; i != points.size(); ++i)
+ {
+ curve.mul(c[i], c[i-1], points[i].m_coord_z, ws);
+ }
+
+ BigInt s_inv = curve.invert_element(c[c.size()-1], ws);
+
+ BigInt z_inv, z2_inv, z3_inv;
+
+ for(size_t i = points.size() - 1; i != 0; i--)
+ {
+ PointGFp& point = points[i];
+
+ curve.mul(z_inv, s_inv, c[i-1], ws);
+
+ s_inv = curve.mul_to_tmp(s_inv, point.m_coord_z, ws);
+
+ curve.sqr(z2_inv, z_inv, ws);
+ curve.mul(z3_inv, z2_inv, z_inv, ws);
+ point.m_coord_x = curve.mul_to_tmp(point.m_coord_x, z2_inv, ws);
+ point.m_coord_y = curve.mul_to_tmp(point.m_coord_y, z3_inv, ws);
+ point.m_coord_z = rep_1;
+ }
+
+ curve.sqr(z2_inv, s_inv, ws);
+ curve.mul(z3_inv, z2_inv, s_inv, ws);
+ points[0].m_coord_x = curve.mul_to_tmp(points[0].m_coord_x, z2_inv, ws);
+ points[0].m_coord_y = curve.mul_to_tmp(points[0].m_coord_y, z3_inv, ws);
+ points[0].m_coord_z = rep_1;
+ }
+
+void PointGFp::force_affine()
+ {
+ if(is_zero())
+ throw Invalid_State("Cannot convert zero ECC point to affine");
+
+ secure_vector<word> ws;
+
+ const BigInt z_inv = m_curve.invert_element(m_coord_z, ws);
+ const BigInt z2_inv = m_curve.sqr_to_tmp(z_inv, ws);
+ const BigInt z3_inv = m_curve.mul_to_tmp(z_inv, z2_inv, ws);
+ m_coord_x = m_curve.mul_to_tmp(m_coord_x, z2_inv, ws);
+ m_coord_y = m_curve.mul_to_tmp(m_coord_y, z3_inv, ws);
+ m_coord_z = m_curve.get_1_rep();
+ }
+
+bool PointGFp::is_affine() const
+ {
+ return m_curve.is_one(m_coord_z);
+ }
+
+BigInt PointGFp::get_affine_x() const
+ {
+ if(is_zero())
+ throw Illegal_Transformation("Cannot convert zero point to affine");
+
+ secure_vector<word> monty_ws;
+
+ if(is_affine())
+ return m_curve.from_rep_to_tmp(m_coord_x, monty_ws);
+
+ BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+ z2 = m_curve.invert_element(z2, monty_ws);
+
+ BigInt r;
+ m_curve.mul(r, m_coord_x, z2, monty_ws);
+ m_curve.from_rep(r, monty_ws);
+ return r;
+ }
+
+BigInt PointGFp::get_affine_y() const
+ {
+ if(is_zero())
+ throw Illegal_Transformation("Cannot convert zero point to affine");
+
+ secure_vector<word> monty_ws;
+
+ if(is_affine())
+ return m_curve.from_rep_to_tmp(m_coord_y, monty_ws);
+
+ const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+ const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
+ const BigInt z3_inv = m_curve.invert_element(z3, monty_ws);
+
+ BigInt r;
+ m_curve.mul(r, m_coord_y, z3_inv, monty_ws);
+ m_curve.from_rep(r, monty_ws);
+ return r;
+ }
+
+bool PointGFp::on_the_curve() const
+ {
+ /*
+ Is the point still on the curve?? (If everything is correct, the
+ point is always on its curve; then the function will return true.
+ If somehow the state is corrupted, which suggests a fault attack
+ (or internal computational error), then return false.
+ */
+ if(is_zero())
+ return true;
+
+ secure_vector<word> monty_ws;
+
+ const BigInt y2 = m_curve.from_rep_to_tmp(m_curve.sqr_to_tmp(m_coord_y, monty_ws), monty_ws);
+ const BigInt x3 = m_curve.mul_to_tmp(m_coord_x, m_curve.sqr_to_tmp(m_coord_x, monty_ws), monty_ws);
+ const BigInt ax = m_curve.mul_to_tmp(m_coord_x, m_curve.get_a_rep(), monty_ws);
+ const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+
+ if(m_coord_z == z2) // Is z equal to 1 (in Montgomery form)?
+ {
+ if(y2 != m_curve.from_rep_to_tmp(x3 + ax + m_curve.get_b_rep(), monty_ws))
+ return false;
+ }
+
+ const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
+ const BigInt ax_z4 = m_curve.mul_to_tmp(ax, m_curve.sqr_to_tmp(z2, monty_ws), monty_ws);
+ const BigInt b_z6 = m_curve.mul_to_tmp(m_curve.get_b_rep(), m_curve.sqr_to_tmp(z3, monty_ws), monty_ws);
+
+ if(y2 != m_curve.from_rep_to_tmp(x3 + ax_z4 + b_z6, monty_ws))
+ return false;
+
+ return true;
+ }
+
+// swaps the states of *this and other, does not throw!
+void PointGFp::swap(PointGFp& other)
+ {
+ m_curve.swap(other.m_curve);
+ m_coord_x.swap(other.m_coord_x);
+ m_coord_y.swap(other.m_coord_y);
+ m_coord_z.swap(other.m_coord_z);
+ }
+
+bool PointGFp::operator==(const PointGFp& other) const
+ {
+ if(m_curve != other.m_curve)
+ return false;
+
+ // If this is zero, only equal if other is also zero
+ if(is_zero())
+ return other.is_zero();
+
+ return (get_affine_x() == other.get_affine_x() &&
+ get_affine_y() == other.get_affine_y());
+ }
+
+// encoding and decoding
+std::vector<uint8_t> PointGFp::encode(PointGFp::Compression_Type format) const
+ {
+ if(is_zero())
+ return std::vector<uint8_t>(1); // single 0 byte
+
+ const size_t p_bytes = m_curve.get_p().bytes();
+
+ const BigInt x = get_affine_x();
+ const BigInt y = get_affine_y();
+
+ std::vector<uint8_t> result;
+
+ if(format == PointGFp::UNCOMPRESSED)
+ {
+ result.resize(1 + 2*p_bytes);
+ result[0] = 0x04;
+ BigInt::encode_1363(&result[1], p_bytes, x);
+ BigInt::encode_1363(&result[1+p_bytes], p_bytes, y);
+ }
+ else if(format == PointGFp::COMPRESSED)
+ {
+ result.resize(1 + p_bytes);
+ result[0] = 0x02 | static_cast<uint8_t>(y.get_bit(0));
+ BigInt::encode_1363(&result[1], p_bytes, x);
+ }
+ else if(format == PointGFp::HYBRID)
+ {
+ result.resize(1 + 2*p_bytes);
+ result[0] = 0x06 | static_cast<uint8_t>(y.get_bit(0));
+ BigInt::encode_1363(&result[1], p_bytes, x);
+ BigInt::encode_1363(&result[1+p_bytes], p_bytes, y);
+ }
+ else
+ throw Invalid_Argument("EC2OSP illegal point encoding");
+
+ return result;
+ }
+
+namespace {
+
+BigInt decompress_point(bool yMod2,
+ const BigInt& x,
+ const BigInt& curve_p,
+ const BigInt& curve_a,
+ const BigInt& curve_b)
+ {
+ BigInt xpow3 = x * x * x;
+
+ BigInt g = curve_a * x;
+ g += xpow3;
+ g += curve_b;
+ g = g % curve_p;
+
+ BigInt z = ressol(g, curve_p);
+
+ if(z < 0)
+ throw Illegal_Point("error during EC point decompression");
+
+ if(z.get_bit(0) != yMod2)
+ z = curve_p - z;
+
+ return z;
+ }
+
+}
+
+PointGFp OS2ECP(const uint8_t data[], size_t data_len,
+ const CurveGFp& curve)
+ {
+ // Should we really be doing this?
+ if(data_len <= 1)
+ return PointGFp(curve); // return zero
+
+ std::pair<BigInt, BigInt> xy = OS2ECP(data, data_len, curve.get_p(), curve.get_a(), curve.get_b());
+
+ PointGFp point(curve, xy.first, xy.second);
+
+ if(!point.on_the_curve())
+ throw Illegal_Point("OS2ECP: Decoded point was not on the curve");
+
+ return point;
+ }
+
+std::pair<BigInt, BigInt> OS2ECP(const uint8_t data[], size_t data_len,
+ const BigInt& curve_p,
+ const BigInt& curve_a,
+ const BigInt& curve_b)
+ {
+ if(data_len <= 1)
+ throw Decoding_Error("OS2ECP invalid point");
+
+ const uint8_t pc = data[0];
+
+ BigInt x, y;
+
+ if(pc == 2 || pc == 3)
+ {
+ //compressed form
+ x = BigInt::decode(&data[1], data_len - 1);
+
+ const bool y_mod_2 = ((pc & 0x01) == 1);
+ y = decompress_point(y_mod_2, x, curve_p, curve_a, curve_b);
+ }
+ else if(pc == 4)
+ {
+ const size_t l = (data_len - 1) / 2;
+
+ // uncompressed form
+ x = BigInt::decode(&data[1], l);
+ y = BigInt::decode(&data[l+1], l);
+ }
+ else if(pc == 6 || pc == 7)
+ {
+ const size_t l = (data_len - 1) / 2;
+
+ // hybrid form
+ x = BigInt::decode(&data[1], l);
+ y = BigInt::decode(&data[l+1], l);
+
+ const bool y_mod_2 = ((pc & 0x01) == 1);
+
+ if(decompress_point(y_mod_2, x, curve_p, curve_a, curve_b) != y)
+ throw Illegal_Point("OS2ECP: Decoding error in hybrid format");
+ }
+ else
+ throw Invalid_Argument("OS2ECP: Unknown format type " + std::to_string(pc));
+
+ return std::make_pair(x, y);
+ }
+
+}