diff options
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.cpp | 731 |
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); + } + +} |