diff --git a/include/ack/ec.hpp b/include/ack/ec.hpp index 8561be6..00240c4 100644 --- a/include/ack/ec.hpp +++ b/include/ack/ec.hpp @@ -368,7 +368,7 @@ namespace ack { template struct ec_point_fp : ec_point_base, CurveT> { - static_assert( is_ec_curve_fp ); + static_assert( is_ec_curve_fp> ); using base_type = ec_point_base, CurveT>; using int_type = typename CurveT::int_type; @@ -470,7 +470,7 @@ namespace ack { return *this; } - // TODO: before this sub operation caused error in wasm: memcpy with overlapping memory memcpy can only accept non-aliasing pointers + // TODO: before this sub operation caused error in wasm: memcpy with overlapping memory memcpy can only accept non-aliasing pointers auto s = a.x - x; if ( s.is_zero() ) { if ( y == a.y ) { // double point @@ -1552,7 +1552,7 @@ namespace ack { /** * Generates a point from base point g and given scalar x. * - * @tparam PointT - point type to create. + * @tparam PointT - EC point type to create. * * @param x - scalar to multiply base point g with. * @return PointT - point on the curve. @@ -1571,7 +1571,7 @@ namespace ack { /** * Generates a point from base point g and given hex string scalar sx. * - * @tparam PointU - point type to create. + * @tparam PointU - EC point type to create. * * @param sx - hex string scalar to multiply base point g with. * @return PointU - point on the curve. @@ -1585,7 +1585,7 @@ namespace ack { /** * Generates a point from base point g and given hex string literal scalar sx. * - * @tparam PointU - point type to create. + * @tparam PointU - EC point type to create. * * @param sx - hex string literal scalar to multiply base point g with. * @return PointU - point on the curve. @@ -1636,7 +1636,7 @@ namespace ack { * @note The returned point can be invalid, since the function allows * creating points that were not generated with the generator point. * - * @tparam PointU - point type to make. Default affine point_type. + * @tparam PointU - EC point type to make. Default affine point_type. * * @param x - point x coordinate * @param y - point y coordinate @@ -1644,7 +1644,7 @@ namespace ack { * Default is false. Slow operation, can be be performed also with call to point.is_valid() function. * @return Curve point */ - template + template [[nodiscard]] constexpr PointU make_point(IntT&& x, IntT&& y, bool verify = false) const { check_integer( x, "Invalid point x coordinate" ); @@ -1665,7 +1665,7 @@ namespace ack { * @note The returned point can be invalid, since the function allows * creating points that were not generated with the generator point. * - * @tparam PointU - point type to make. Default affine point_type. + * @tparam PointU - EC point type to make. Default affine point_type. * * @param x - point x coordinate * @param y - point y coordinate @@ -1686,6 +1686,156 @@ namespace ack { return point; } + /** + * Solves the equation y = sqrt( x^3 + ax + b ) (mod p) and returns y. + * + * @param x - field element x-coordinate. + * @param odd - True if the calculated y-coordinate should be odd. + * @return The y-coordinate, or 0 if no square root exists. + */ + [[nodiscard]] + inline field_element_type compute_y(const field_element_type& x, const bool odd) const { + auto y = (( x * x + a ) * x + b ).sqrt(); + if ( odd != y.value().test_bit( 0 )) { + y = -y; + } + return y; + } + + /** + * Solves the equation y = sqrt( x^3 + ax + b ) (mod p) and returns y. + * + * @param x - integer x-coordinate. + * @param odd - True if the calculated y-coordinate should be odd. + * @param verify - True if x coordinate should be verified before calculating y. Default false. + * @return The y-coordinate, or 0 if no square root exists. + */ + [[nodiscard]] + inline field_element_type compute_y(const IntT& x, const bool odd, bool verify = false) const { + return compute_y( make_field_element( x, verify ), odd ); + } + + /** + * Decompress point from provided x-coordinate. + * @tparam PointT - returned EC point type. + * + * @param x - field element x-coordinate. + * @param yodd - True if the calculated y-coordinate should be odd. + * @return The decompressed EC point. If the y-coordinate cannot be computed, the point at infinity is returned. + */ + template + [[nodiscard]] + inline PointT decompress_point(field_element_type x, const bool yodd) const { + auto y = compute_y( x, yodd ); + if ( y.is_zero() ) { + return PointT{}; + } + return make_point( std::move( x ), y ); + } + + /** + * Decompress point from provided x-coordinate. + * @tparam PointT - returned EC point type. + * + * @param x - integer x-coordinate. + * @param yodd - True if the calculated y-coordinate should be odd. + * @param verify - True if x coordinate should be verified before calculating y. Default false. + * @return The decompressed EC point. If the y-coordinate cannot be computed, the point at infinity is returned. + */ + template + [[nodiscard]] + inline PointT decompress_point(const IntT& x, const bool yodd, bool verify = false) const { + return decompress_point( make_field_element( x, verify ), yodd ); + } + + /** + * Encodes an EC point into its byte-encoded form. + * + * This function follows the SEC1-v2 standard for EC point encoding. + * The 'compress' parameter determines whether to use compressed or uncompressed + * point encoding. Compressed encoding includes only the x-coordinate, while uncompressed encoding + * includes both the x and y coordinates. + * + * Encoded Format: | [x] | [y] + * type: + * 0 - point at infinity, not coordinates is encoded + * 2 - compressed form with only x-coordinate (even y-coordinate is derived) + * 3 - compressed form with only x-coordinate (odd y-coordinate is derived) + * 4 - uncompressed form with both x and y coordinates + * + * @see SEC1-v2 section '2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion' + * https://www.secg.org/sec1-v2.pdf#page=16 + * + * @param point - The EC point to be encoded, represented as a `point_type` structure. + * @param compress - If true the encoded point will be in compressed form; otherwise, encodes it in uncompressed form. + * @return The byte-encoded form of the EC point. + */ + [[nodiscard]] bytes encode_point(const point_type& point, bool compress) const + { + bytes epoint = { 0 }; + if ( point.is_identity() ) { + return epoint; + } + + if ( compress ) { + epoint[0] = 2 + point.y.value().test_bit( 0 ); + epoint += point.x.to_bytes(); + } + else { + epoint[0] = 4; + epoint += point.x.to_bytes() + point.y.to_bytes(); + } + return epoint; + } + + /** + * Decodes an EC point from its byte-encoded form. + * + * This function follows the SEC1-v2 standard for EC point encoding. The encoded point + * is expected to be in the format (type | x [| y]), where 'type' represents the encoding type or + * parity of the y-coordinate (if compressed form), and 'x' & 'y' are coordinates of the point. + * + * @see SEC1-v2 section '2.3.4 Octet-String-to-Elliptic-Curve-Point Conversion' + * https://www.secg.org/sec1-v2.pdf#page=17 + * + * @see encode_point + * + * @note internally function halts if epoint is in invalid format or encoded coordinates are invalid. + * + * @tparam PointT - returned EC point type. + * + * @param epoint A bytes view of the encoded EC point (type | x [| y]). + * @return The decoded EC point of type PointT. + */ + template + [[nodiscard]] PointT decode_point(const bytes_view epoint) const + { + check( !epoint.empty(), "invalid encoded point" ); + + const auto type = epoint[0]; + check( type <= 4 && type != 1, "invalid encoded point" ); + + const std::size_t plen = p.byte_length(); + PointT P; // type 0 = point at infinity + switch ( type ) { + // 2.3.4.2 + case 2: + case 3: { + check( epoint.size() == plen + 1, "invalid encoded point" ); + P = decompress_point( /*x=*/epoint.subspan( 1, plen ), /*yodd=*/type & 1 ); + } break; + // 2.3.4.3 + case 4: { + check( epoint.size() == 2 * plen + 1, "invalid encoded point" ); + P = make_point( + epoint.subspan( 1, plen ), + epoint.subspan( 1 + plen, plen ) + ); + } break; + } + return P; + } + /** * Verifies curve parameters. * @note Very basic verification is performed, no check for primality of p and n; diff --git a/tests/include/ack/tests/ec_test.hpp b/tests/include/ack/tests/ec_test.hpp index 545660e..601e4a8 100644 --- a/tests/include/ack/tests/ec_test.hpp +++ b/tests/include/ack/tests/ec_test.hpp @@ -4418,8 +4418,293 @@ namespace ack::tests { } EOSIO_TEST_END // ec_keypair_secp256r1_test - EOSIO_TEST_BEGIN( ec_mul_add_fast_test ) + EOSIO_TEST_BEGIN( ec_misc ) using namespace detail; + + // Test solving curve quadratic equation via `compute_y` function + + { + // Test vectors were generated for sepc256r1 using OpenSSL 3.0.2 + { + auto tv_x = "5cd5c3fb57668b0d6c85543fe480167324ff58a646adfb22a051913f125cf03a"_hex; + auto x = secp256r1.make_field_element( tv_x ); + auto y = secp256r1.compute_y( x , /*odd=*/ true ); + REQUIRE_EQUAL( y, "dbd82d3535a54276e9fff543420ee688c4e3affeaa44d1b6ca755325ffeead7d"_hex ) + y = secp256r1.compute_y( tv_x , /*odd=*/ true ); + REQUIRE_EQUAL( y, "dbd82d3535a54276e9fff543420ee688c4e3affeaa44d1b6ca755325ffeead7d"_hex ) + + tv_x = "ae5af2a936e7fe8dee15ea42c1a568687749b7025937277381993fcd9b80de7a"_hex; + x = secp256r1.make_field_element( tv_x ); + y = secp256r1.compute_y( x , /*odd=*/ false ); + REQUIRE_EQUAL( y, "052bd863421e18a159f38b69f5847a1a3bcc54441ee4bde043b740c91f24b6d2"_hex ) + y = secp256r1.compute_y( tv_x , /*odd=*/ false ); + REQUIRE_EQUAL( y, "052bd863421e18a159f38b69f5847a1a3bcc54441ee4bde043b740c91f24b6d2"_hex ) + } + + // Test vectors were generated for secp256k1 using OpenSSL 3.0.2 + { + auto tv_x = "d2e338aa2899f499525af33e7fe650867d07c31221e77b184161f728a3cfed42"_hex; + auto x = secp256k1.make_field_element( tv_x ); + auto y = secp256k1.compute_y( x , /*odd=*/ true ); + REQUIRE_EQUAL( y, "13dcec036f6b04fad74ff8067213031d03c288f222962519ee7ef0c171e97d77"_hex ) + y = secp256k1.compute_y( tv_x , /*odd=*/ true ); + REQUIRE_EQUAL( y, "13dcec036f6b04fad74ff8067213031d03c288f222962519ee7ef0c171e97d77"_hex ) + + tv_x = "c1ed2c8fa1fc2d913712547a5439c62ddb4dee08ccd1d585c0cdee87433c7ab3"_hex; + x = secp256k1.make_field_element( tv_x ); + y = secp256k1.compute_y( x , /*odd=*/ false ); + REQUIRE_EQUAL( y, "9e3bbc0096832c16a3d1c6573aa2160a2d6c276fc8a5fc74ddb3c92585ede554"_hex ) + y = secp256k1.compute_y( tv_x , /*odd=*/ false ); + REQUIRE_EQUAL( y, "9e3bbc0096832c16a3d1c6573aa2160a2d6c276fc8a5fc74ddb3c92585ede554"_hex ) + } + } + + // Test decompressing point from x-coordinate + // i.e.: point decompression + { + // Test vectors were generated for sepc256r1 using OpenSSL 3.0.2 + { + auto tv_x = "5cd5c3fb57668b0d6c85543fe480167324ff58a646adfb22a051913f125cf03a"_hex; + auto x = secp256r1.make_field_element( tv_x ); + auto p = secp256r1.decompress_point( x , /*odd=*/ true ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "dbd82d3535a54276e9fff543420ee688c4e3affeaa44d1b6ca755325ffeead7d"_hex ) + p = secp256r1.decompress_point( tv_x , /*odd=*/ true ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "dbd82d3535a54276e9fff543420ee688c4e3affeaa44d1b6ca755325ffeead7d"_hex ) + + tv_x = "ae5af2a936e7fe8dee15ea42c1a568687749b7025937277381993fcd9b80de7a"_hex; + x = secp256r1.make_field_element( "ae5af2a936e7fe8dee15ea42c1a568687749b7025937277381993fcd9b80de7a"_hex ); + p = secp256r1.decompress_point( x , /*odd=*/ false ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "052bd863421e18a159f38b69f5847a1a3bcc54441ee4bde043b740c91f24b6d2"_hex ) + p = secp256r1.decompress_point( tv_x , /*odd=*/ false ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "052bd863421e18a159f38b69f5847a1a3bcc54441ee4bde043b740c91f24b6d2"_hex ) + } + + // Test vectors were generated for secp256k1 using OpenSSL 3.0.2 + { + auto tv_x = "d2e338aa2899f499525af33e7fe650867d07c31221e77b184161f728a3cfed42"_hex; + auto x = secp256k1.make_field_element( tv_x ); + auto p = secp256k1.decompress_point( x , /*odd=*/ true ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "13dcec036f6b04fad74ff8067213031d03c288f222962519ee7ef0c171e97d77"_hex ) + p = secp256k1.decompress_point( tv_x , /*odd=*/ true ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "13dcec036f6b04fad74ff8067213031d03c288f222962519ee7ef0c171e97d77"_hex ) + + tv_x = "c1ed2c8fa1fc2d913712547a5439c62ddb4dee08ccd1d585c0cdee87433c7ab3"_hex; + x = secp256k1.make_field_element( tv_x ); + p = secp256k1.decompress_point( x , /*odd=*/ false ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "9e3bbc0096832c16a3d1c6573aa2160a2d6c276fc8a5fc74ddb3c92585ede554"_hex ) + p = secp256k1.decompress_point( tv_x , /*odd=*/ false ); + REQUIRE_EQUAL( p.x, x ) + REQUIRE_EQUAL( p.y, "9e3bbc0096832c16a3d1c6573aa2160a2d6c276fc8a5fc74ddb3c92585ede554"_hex ) + } + + // Test function decompress_point returns point at infinity when y coordinate can't be computed + REQUIRE_EQUAL( secp256k1.decompress_point( "0"_hex, false ).is_identity(), true ) + REQUIRE_EQUAL( secp256k1.decompress_point( "0"_hex, true ).is_identity(), true ) + REQUIRE_EQUAL( secp256k1.decompress_point( secp256k1.make_field_element( "0"_hex ), false ).is_identity(), true ) + REQUIRE_EQUAL( secp256k1.decompress_point( secp256k1.make_field_element( "0"_hex ), true ).is_identity(), true ) + } + + // Test encoding/decoding EC point + { + // Test decoding point at infinity + REQUIRE_EQUAL( secp256k1.decode_point( "00"_hex ).is_identity(), true ) + REQUIRE_EQUAL( secp256r1.decode_point( "00"_hex ).is_identity(), true ) + REQUIRE_EQUAL( secp256k1.decode_point( "00"_hex ).is_identity(), true ) + REQUIRE_EQUAL( secp256k1.decode_point( "00"_hex ).is_identity(), true ) + + // Test decoding point at infinity succeeds when additional data is appended to encode type (1st byte) + REQUIRE_EQUAL( secp256r1.decode_point( "00BADDA7A"_hex ).is_identity(), true ) + + // Test decoding invalid point coordinates results in pointy at infinity + REQUIRE_EQUAL( secp256k1.decode_point( "020000000000000000000000000000000000000000000000000000000000000000"_hex ).is_identity(), true ) + REQUIRE_EQUAL( secp256k1.decode_point( "030000000000000000000000000000000000000000000000000000000000000000"_hex ).is_identity(), true ) + REQUIRE_EQUAL( secp256k1.decode_point( "0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"_hex ).is_identity(), true ) + + // Test vectors were generated for secp256r1 using OpenSSL 3.0.2 + { + auto tv = "035cd5c3fb57668b0d6c85543fe480167324ff58a646adfb22a051913f125cf03a"_hex; + auto p = secp256r1.decode_point( tv ); + REQUIRE_EQUAL( p.x, "5cd5c3fb57668b0d6c85543fe480167324ff58a646adfb22a051913f125cf03a"_hex ) + REQUIRE_EQUAL( p.y, "dbd82d3535a54276e9fff543420ee688c4e3affeaa44d1b6ca755325ffeead7d"_hex ) + + auto p_proj = secp256r1.decode_point( tv ); + REQUIRE_EQUAL( p_proj.to_affine(), p ) + + auto p_jacobi = secp256r1.decode_point( tv ); + REQUIRE_EQUAL( p_jacobi.to_affine(), p) + + + auto ep = secp256r1.encode_point( p, /*compressed=*/ false ); + REQUIRE_EQUAL( ep, "045cd5c3fb57668b0d6c85543fe480167324ff58a646adfb22a051913f125cf03adbd82d3535a54276e9fff543420ee688c4e3affeaa44d1b6ca755325ffeead7d"_hex ) + + ep = secp256r1.encode_point( p, /*compressed=*/ true ); + REQUIRE_EQUAL( ep, tv ) + } + + // Test vectors were generated for secp256r1 using OpenSSL 3.0.2 + { + auto tv = "02ae5af2a936e7fe8dee15ea42c1a568687749b7025937277381993fcd9b80de7a"_hex; + auto p = secp256r1.decode_point( tv ); + REQUIRE_EQUAL( p.x, "ae5af2a936e7fe8dee15ea42c1a568687749b7025937277381993fcd9b80de7a"_hex ) + REQUIRE_EQUAL( p.y, "052bd863421e18a159f38b69f5847a1a3bcc54441ee4bde043b740c91f24b6d2"_hex ) + + auto p_proj = secp256r1.decode_point( tv ); + REQUIRE_EQUAL( p_proj.to_affine(), p ) + + auto p_jacobi = secp256r1.decode_point( tv ); + REQUIRE_EQUAL( p_jacobi.to_affine(), p) + + auto ep = secp256r1.encode_point( p, /*compressed=*/ false ); + REQUIRE_EQUAL( ep, "04ae5af2a936e7fe8dee15ea42c1a568687749b7025937277381993fcd9b80de7a052bd863421e18a159f38b69f5847a1a3bcc54441ee4bde043b740c91f24b6d2"_hex ) + + ep = secp256r1.encode_point( p, /*compressed=*/ true ); + REQUIRE_EQUAL( ep, tv ) + } + + // Test vectors were generated for secp256k1 using OpenSSL 3.0.2 + { + auto tv = "03d2e338aa2899f499525af33e7fe650867d07c31221e77b184161f728a3cfed42"_hex; + auto p = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p.x, "d2e338aa2899f499525af33e7fe650867d07c31221e77b184161f728a3cfed42"_hex ) + REQUIRE_EQUAL( p.y, "13dcec036f6b04fad74ff8067213031d03c288f222962519ee7ef0c171e97d77"_hex ) + + auto p_proj = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p_proj.to_affine(), p ) + + auto p_jacobi = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p_jacobi.to_affine(), p) + + auto ep = secp256k1.encode_point( p, /*compressed=*/ false ); + REQUIRE_EQUAL( ep, "04d2e338aa2899f499525af33e7fe650867d07c31221e77b184161f728a3cfed4213dcec036f6b04fad74ff8067213031d03c288f222962519ee7ef0c171e97d77"_hex ) + + ep = secp256k1.encode_point( p, /*compressed=*/ true ); + REQUIRE_EQUAL( ep, tv ) + } + + // Test vectors were generated for secp256k1 using OpenSSL 3.0.2 + { + auto tv = "02c1ed2c8fa1fc2d913712547a5439c62ddb4dee08ccd1d585c0cdee87433c7ab3"_hex; + auto p = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p.x, "c1ed2c8fa1fc2d913712547a5439c62ddb4dee08ccd1d585c0cdee87433c7ab3"_hex ) + REQUIRE_EQUAL( p.y, "9e3bbc0096832c16a3d1c6573aa2160a2d6c276fc8a5fc74ddb3c92585ede554"_hex ) + + auto p_proj = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p_proj.to_affine(), p ) + + auto p_jacobi = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p_jacobi.to_affine(), p) + + auto ep = secp256k1.encode_point( p, /*compressed=*/ false ); + REQUIRE_EQUAL( ep, "04c1ed2c8fa1fc2d913712547a5439c62ddb4dee08ccd1d585c0cdee87433c7ab39e3bbc0096832c16a3d1c6573aa2160a2d6c276fc8a5fc74ddb3c92585ede554"_hex ) + + ep = secp256k1.encode_point( p, /*compressed=*/ true ); + REQUIRE_EQUAL( ep, tv ) + } + + // Test vectors were generated for secp256k1 using OpenSSL 3.0.2 + { + auto tv = "0200b824dd20143dc94d4242db1d14ae3dc08b732c5a3c90b4cf3c33475dc16674"_hex; + auto p = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p.x, "b824dd20143dc94d4242db1d14ae3dc08b732c5a3c90b4cf3c33475dc16674"_hex ) + REQUIRE_EQUAL( p.y, "56f4d5f1e4e9586a060926f5776f0f8d570d710589ae2d7126542167194a291e"_hex ) + + auto p_proj = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p_proj.to_affine(), p ) + + auto p_jacobi = secp256k1.decode_point( tv ); + REQUIRE_EQUAL( p_jacobi.to_affine(), p) + + auto ep = secp256k1.encode_point( p, /*compressed=*/ false ); + REQUIRE_EQUAL( ep, "0400b824dd20143dc94d4242db1d14ae3dc08b732c5a3c90b4cf3c33475dc1667456f4d5f1e4e9586a060926f5776f0f8d570d710589ae2d7126542167194a291e"_hex ) + + ep = secp256k1.encode_point( p, /*compressed=*/ true ); + REQUIRE_EQUAL( ep, tv ) + } + + // Fail tests + { + // Invalid type test + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( bytes() ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "01"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "05"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "08"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "10"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "12"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "22"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "42"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "48"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "FC"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "FD"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "FE"_hex ); + }) + + // Valid type but too little data to decode tests + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "02"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "03"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + auto p1 = secp256k1.decode_point( "04"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + // x-coord missing zero padding to match the length of curve's p + auto p1 = secp256k1.decode_point( "02b824dd20143dc94d4242db1d14ae3dc08b732c5a3c90b4cf3c33475dc16674"_hex ); + }) + + REQUIRE_ASSERT( "invalid encoded point", [&]() { + // x-coord missing zero padding to match the length of curve's p + auto p1 = secp256k1.decode_point( "04b824dd20143dc94d4242db1d14ae3dc08b732c5a3c90b4cf3c33475dc1667456f4d5f1e4e9586a060926f5776f0f8d570d710589ae2d7126542167194a291e"_hex ); + }) + } + } + + // Test ec_mul_add_fast for_each_curve_do( [](const CurveT& c) { for ( int32_t i = 1; i < 101; i++) { auto p = c.g * i; @@ -4455,6 +4740,6 @@ namespace ack::tests { EOSIO_TEST( ec_mul_secp256k1_test ) EOSIO_TEST( ec_mul_secp256r1_test ) EOSIO_TEST( ec_keypair_secp256r1_test ) - EOSIO_TEST( ec_mul_add_fast_test ) + EOSIO_TEST( ec_misc ) EOSIO_TEST_END }