Author

Topic: Adding points on an Elliptic curve (Read 1844 times)

member
Activity: 348
Merit: 34
April 22, 2021, 02:36:43 PM
#8
abc
023ef30130654689a64c864d6dd38760481c55fc525e2c6c7084e2d2d3d4d51be9
043ef30130654689a64c864d6dd38760481c55fc525e2c6c7084e2d2d3d4d51be9f7d86b288c09d db5311f292285168000e43e4b62201bd8de23a391daa8e00ce8

def
036a6e1dc6f203f7fdd97965892301e5fb995a37318c410543835f0edcd3456c49
046a6e1dc6f203f7fdd97965892301e5fb995a37318c410543835f0edcd3456c492a072b9898b93 e9eb05f9ad86a97546d83b579bf6efd3482f93baca13784496b

abcdef
0312faae608bd6562562b8f85564664cd1fdcd667f6b24b2b221ef86b9231f4d74
0412faae608bd6562562b8f85564664cd1fdcd667f6b24b2b221ef86b9231f4d74512ee8cd9b343 31afd05ccb8d81d1393c150c73ec5695845b731f7e6e0086719

here abc is 1 point, and 2nd point is def
if we can join or merge like abc add to def
where result would be abcdef point

abc+def = 18AB
one new func could be inside
like abc+def = abcdef
like point +point = pp ( line extended) ( not 2p)

point 1
point 2
a =1+2 = 3
more advance looking func

point 1
point 2
a =1+2 = 12

any ecc expert, can explain about this addition ?
member
Activity: 65
Merit: 16
March 31, 2015, 09:41:27 PM
#7
OK I finally found 2 errors:
1) In my code I cannot have a negative value for the denominator
of the slope. I just have to multiply both the numerator and the denominator by -1
2) I use as p the value given by royalforkblog.com and this is not the value of p
but instead the order of the curve (the number of points on the curve).
The correct value is given by gmaxwell in a previous post.

Now it works !
 Grin

Last (optional) question:

for an elliptique curve, the order of the curve and the prime number p
should be the same only if p mod 4 = 3
I try 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F mod 4
= 115792089237316195423570985008687907853269984665640564039457584007908834671663 mod 4
and it is equal to 3 ! So they should be the same ?
member
Activity: 65
Merit: 16
March 28, 2015, 08:55:11 PM
#6
My goal is to understand the first principles.
Once I will be able to reproduce the results, I will no longer need
to use my code but instead I will use the code implemented in bitcoin.
As a physicist, I use a lot of math (continuous functions)
but I’m not a mathematician. So I don’t want to become
a cryptographer but the question is how far should I step back?

For the next days, I will write test cases for extended euclidean algorithm,
adding points on EC and my add and multiply algorithm to figure out where is my error.

Sage works perfectly :-)
staff
Activity: 4284
Merit: 8808
March 27, 2015, 05:41:59 AM
#5
I see at least one error in your addition implementation (I can point it out if you really want, but I think if your goal is to learn, you will learn more if I don't; if your goal is to get something working I suggest rethinking your goal)..., What are you trying to accomplish?  If you want to make an implementation to actually use your approach (kludging together a result from semi-tech popular press books, rather than stepping back and understanding first principles) will be horrifyingly slow and likely end up broken or insecure.

If you're just trying to learn, you should probably step back and work on each component one at a time so you know exactly where your error is, and so you can gain some understanding. E.g. write tests for your field multiplies, field adds, write a test for your modular inverse (try lots of numbers, invert and multiply by itself to check the identity).  You don't necessarily have to have the answers to check against... check the algebraic identities.  e.g., for points:

A = G + G
B = A + G
C = B + G
D = C + G
B == D + -A
D == B + A
A == C + -A
A + C  == B + B
Infinity = C + -C.
A + D + -A == -D + D + D
... and so on.

Though it's perfectly possible to build a correct implementation with no known value vectors; if you really want them I would suggest I suggest installing (or getting web access to) sage (sagemath), as it has a fine implementation of elliptic curves on finite fields and can easily be used as a 'pocket calculator' to check your results: E.g.

sage: F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
sage: C = EllipticCurve ([F (0), F (7)])
sage: G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
sage: G
( 55066263022277343669578718895168534326250603453777594175500187360389116729240 : 32670510020758816978083085130507043184471273380659243275938904335757337482424 : 1)
sage: G+G
( 89565891926547004231252920425935692360644145829622209833684329913297188986597 : 12158399299693830322967808612713398636155367887041628176798871954788371653930 : 1)
sage: G+G+G
(112711660439710606056748659173929673102114977341539408544630613555209775888121 : 25583027980570883691656905877401976406448868254816295069919888960541586679410 : 1)
sage: 8675309*G
( 66641067246008511739397675128206923493293851901978595085468284019495272794983 : 22882405661336615738255795181502754819791112635438119114432507482219105379189 : 1)
sage: 8675309*G*0x5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72
(102906664656524967437285391368203362606397786797828404077808951036579554576623 : 22882405661336615738255795181502754819791112635438119114432507482219105379189 : 1)
sage: 8675309*G*0x5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72^2
( 62036446572098911670458903520965529606848330631474128915637932959742841971720 : 22882405661336615738255795181502754819791112635438119114432507482219105379189 : 1)
sage: 8675309*G*0x5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72^3
( 66641067246008511739397675128206923493293851901978595085468284019495272794983 : 22882405661336615738255795181502754819791112635438119114432507482219105379189 : 1)

Though there are other high quality implementations of secp256k1 in C, ... since you seem comfortable using a C derived language, why not just use another implementation to get your test cases?
member
Activity: 65
Merit: 16
March 26, 2015, 10:34:48 PM
#4
I implemented a solution using the Extended Euclidien algorithm,
I have the right results with small numbers but not with large numbers
Here is my solution:

I can add any points whitout problems, but with large numbers,
using http://www.royalforkblog.com/2014/09/04/ecc/, I get
Code:
WRONG
privKeyDec: 23695310554196047209792918797392416191148132060917476866274911959533140016553
privKeyHex: 3463120c802f8bd7972aa996083fec011f387a61e55b7b507b9c68d2bc9f7da9

public key = privKeyDec * G:
pubKeyDec_x = 60306455975408622297239001036599486600287350729488590738949419698511411824492
pubKeyDec_y = 105340305216767447115881521734850141742815850841650003460985843120741726629417

pubKeyHex_x = 85543e964d2c63bb7caf42b8dedfa6b3e5a0d99bce28509472654c696a6a476c
pubKeyHex_y = e8e47ff840d46305bb1dc2ff173c14cefa0f7dc6a0d26fb10de264c243a52e29
The results on the web page are:
Code:
OK (?)
pubKeyDec_x = 39874617776630327813190058413816560767734954098998567043224950074533143699292
pubKeyDec_y = 83115399533222200534442050051826386603242609920409430626876080623730665355556
or in hexa:
pubKeyHex_x = 58283bdf22456fffaa0b5f56abd7ad271815d31e44272c0460398540758cdf5c
pubKeyHex_y = b7c1a627a79a9000235382c650f23b6a7ebcd18529667dd674185d295e367924
I also have a wrong result with (Antonopoulos)
http://chimera.labs.oreilly.com/books/1234000001802/ch04.html#public_key_derivation
Can you see the problem in my code (below) or at least can somebody give me
the result of the addition of 2 points: 1 * G + 1 * G with Bitcoin parameters, then I will be able
to track my code because now I have the result of a very large number of additions.
Code:
// g++ ellipticCurve.cpp -lgmpxx -lgmp -std=c++11
#include
#include
#include
#include
#include
#include
#include

using namespace std;

typedef pair point, fraction;
typedef pair fraction;

fraction computeSlope (point, point);
point addPoints (point, point);
string formatPoint (point);
string char2bin (string);
point doubledPoints (point);
tuple ExtGCD (mpz_class, mpz_class);
mpz_class frac2int (point);
string bigNumber2strHex (mpz_class);
mpz_class hexa2BigNum (const string);


// uncomment one of those 2 groups of 4 parameters
// parameters for Bitcoin
mpz_class baseEC("115792089237316195423570985008687907852837564279074904382605163141518161494337");
mpz_class G_x("55066263022277343669578718895168534326250603453777594175500187360389116729240");
mpz_class G_y("32670510020758816978083085130507043184471273380659243275938904335757337482424");
int a = 0;    // EC: y^2 = x^3 + 7

// parameters for http://www.royalforkblog.com/2014/09/04/ecc/
// mpz_class baseEC("29"); // change privKeyHex < 29 (0x1D)
// mpz_class G_x("0");
// mpz_class G_y("2");
// int a = -3;    // EC: y^2 = x^3 -3 x + 4

point pointG(G_x, G_y);
point pointP(0, 0);


main () {
// uncomment one of those 3 groups of 2 parameters

// from: http://chimera.labs.oreilly.com/books/1234000001802/ch04.html#public_key_derivation (Antonopoulos)
// string privKeyHex     = "1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD";
// mpz_class privKeyDec  = hexa2BigNum(privKeyHex);

// from: http://www.royalforkblog.com/2014/09/04/ecc/    (big numbers)
mpz_class privKeyDec("23695310554196047209792918797392416191148132060917476866274911959533140016553");
string privKeyHex = bigNumber2strHex(privKeyDec);
 
// from: http://www.royalforkblog.com/2014/09/04/ecc/    (small numbers)
// mpz_class privKeyDec("26");
// string privKeyHex = bigNumber2strHex(privKeyDec);   // = "1A"

// add and double algorithm : privKey = pubKey * G
mpz_class totG = 0; // keep track of "x2" and "+1" operations
for (size_t i = 0; i < privKeyHex.size(); i++) {
string theCharBin = char2bin(privKeyHex.substr (i,1));
for (size_t j = 0; j <= 3; j++) {
totG += totG;  // double (x2)
pointP = doubledPoints(pointP);

if (theCharBin[j] == '1') {
totG += 1;  // add (+1)
pointP = addPoints(pointG, pointP);
}
}
}
cout << endl ;
//cout << "totG should be = privKeyDec:" << endl;;
//cout << "totG =      " << totG << endl;
cout << "privKeyDec: " << privKeyDec << endl;
cout << "privKeyHex: " << privKeyHex << endl;
cout << endl << "public key = privKeyDec * G:" << endl;
cout << "pubKeyDec_x = " << pointP.first << endl;
cout << "pubKeyDec_y = " << pointP.second << endl << endl;
cout << "pubKeyHex_x = " << bigNumber2strHex(pointP.first) << endl;
cout << "pubKeyHex_y = " << bigNumber2strHex(pointP.second) << endl << endl;
}


point addPoints(point point1, point point2) {

point point3, x3_frac, y3_frac;

if (point1.first == 0 && point1.second == 0) {
return point2;
}
if (point2.first == 0 && point2.second == 0) {
return point1;
}

fraction slope = computeSlope(point1, point2);

x3_frac.second = slope.second * slope.second;
x3_frac.first  = slope.first * slope.first - ((point1.first + point2.first) * x3_frac.second);

x3_frac.first = x3_frac.first % baseEC;
x3_frac.first = (x3_frac.first < 0) ? x3_frac.first + baseEC : x3_frac.first;

point3.first  = frac2int(x3_frac);

y3_frac.first = slope.first * (point1.first - point3.first) - point1.second * slope.second;
y3_frac.second = slope.second;

y3_frac.first = y3_frac.first % baseEC;
y3_frac.first = (y3_frac.first < 0) ? y3_frac.first + baseEC : y3_frac.first;

point3.second = frac2int(y3_frac);

return point3;
}

mpz_class frac2int(point theFrac) {
mpz_class u;
tie(ignore, u, ignore) = ExtGCD(theFrac.second, baseEC);
u = (u < 0) ? u + baseEC : u;
return (u * theFrac.first) % baseEC;
}

point doubledPoints(point point1) {
return addPoints(point1, point1);
}

fraction computeSlope(point pointA, point pointB) {
// EC: y^2 = x^3 + 7      // Bitcoin
// EC: y^2 = x^3 -3x + 4  // royalforkblog.com
fraction slope;
if (pointA == pointB) {
slope.first  = 3 * pointA.first * pointA.first + a;
slope.second = 2 * pointA.second;
}
else {
slope.first  = pointB.second - pointA.second; // delta y
slope.second = pointB.first - pointA.first;   // delta x
}
return slope;
}

string formatPoint(point thePoint) {
    ostringstream outs;
    outs << "(" << thePoint.first << ", " << thePoint.second << ")";
   return outs.str( );
}

string char2bin (string theChar) {
int value;

  istringstream ost(theChar);
  ost >> hex >> value;
 
  bitset<4> bits(value);
  string binaryString (bits.to_string() );

return binaryString;
}

tuple ExtGCD (mpz_class a, mpz_class b) {
    if (a == 0)
        return make_tuple(b, 0, 1);
    mpz_class g, y, x;
    tie(g, y, x) = ExtGCD(b % a, a);
    return make_tuple(g, x-(b/a)*y, y);
}

string bigNumber2strHex (mpz_class theBigNumber) {
ostringstream stream;
stream << hex << theBigNumber;
return stream.str();
}

mpz_class hexa2BigNum (const string myString)
{
mpz_class x;   
stringstream stream;
stream << hex << myString;
stream >> x;
return x;
}
member
Activity: 65
Merit: 16
March 25, 2015, 08:38:30 AM
#3
I'm using c++ with gmp for large numbers.
I have all the tools now.
Thanks
:-)
legendary
Activity: 1232
Merit: 1094
March 24, 2015, 03:53:41 PM
#2
You need to multiply both sides by the inverse of x3.

What language are you using?  There is probably a function for computing the modular inverse.

The normal algorithm used is the Extended Euclidean algorithm.

If you just have a way to handle large numbers, you could use the following formula:

x_inv mod p = xp-2 mod p

Lookup "modular exponentiation".
member
Activity: 65
Merit: 16
March 24, 2015, 02:24:42 PM
#1
I want to calculate the public key from the private key.
I can add points on an Elliptic curve with small p, but
I have a problem when I want to use large numbers.
Here is the description of the problem:
Jump to: