Bitcoin Forum
May 28, 2024, 05:31:06 AM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Adding points on an Elliptic curve  (Read 1819 times)
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 24, 2015, 07: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:
TierNolan
Legendary
*
Offline Offline

Activity: 1232
Merit: 1084


View Profile
March 24, 2015, 08: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".

1LxbG5cKXzTwZg9mjL3gaRE835uNQEteWF
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 25, 2015, 01:38:30 PM
 #3

I'm using c++ with gmp for large numbers.
I have all the tools now.
Thanks
:-)
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 27, 2015, 03:34:48 AM
 #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 <stdio.h>
#include <bitset>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <tuple>
#include <gmpxx.h>

using namespace std;

typedef pair<mpz_class, mpz_class> point, fraction;
typedef pair<mpz_class, mpz_class> fraction;

fraction computeSlope (point, point);
point addPoints (point, point);
string formatPoint (point);
string char2bin (string);
point doubledPoints (point);
tuple<mpz_class, mpz_class, mpz_class> 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<mpz_class, mpz_class, mpz_class> 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;
}
gmaxwell
Moderator
Legendary
*
expert
Offline Offline

Activity: 4186
Merit: 8435



View Profile WWW
March 27, 2015, 10:41:59 AM
Last edit: March 27, 2015, 10:54:55 AM by gmaxwell
 #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?
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 29, 2015, 01:55:11 AM
 #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 :-)
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
April 01, 2015, 02:41:27 AM
 #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 ?
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!