Bluffers Guide to EC Signatures: ECDSA is Messy and Does Not Scale Well and EdDSA is Well Crafted…

With a digital signature, we sign a message with a private key (sk), and then prove it with the related public key (pk). For elliptic curve…

Bluffers Guide to EC Signatures: ECDSA is Messy and Does Not Scale Well and EdDSA is Well Crafted and Scales Up

With a digital signature, we sign a message with a private key (sk), and then prove it with the related public key (pk). For elliptic curve signatures we normally have a signature that takes the form of (r,s):

In this case, we will generate signatures for the main methods used in ECDSA and EdDSA using C# and the Bouncy Castle library. A core difference between ECDSA and EdDSA, is that ECDSA requires a random nonce value (k), and EdDSA does not. Overall, we will find ECDSA quite messy in its implementation, while EdDSA is much simplier, and scalable.

ECDSA

For ECDSA, Alice signs the message with the following:

  1. Create a hash of the message e=HASH(m).
  2. Let h be the Ln be the leftmost bits of e, Ln has a bit length of the group order N.
  3. Create a random number k which is between 1 and N−1.
  4. Calculate a point on the curve as (x1,y1)=k×G.
  5. Calculate r=x_1 (mod N). If r=0, go back to Step 3.
  6. Calculate s=k^{−1}(h+rdA)(mod N). If s=0, go back to Step 3.
  7. The signature is the pair (r,s)

Bob will check with:

  1. Create a hash of the message e=HASH(m)
  2. Let h be the Ln leftmost bits of e.
  3. Calculate c=s^{−1} (mod N), and where N is the order of the curve.
  4. Calculate u_1=hc(modN) and u_2=rc(modN)
  5. Calculate the curve point (x1,y1)=uG+uQA
  6. The signature is valid if rx1(modn), invalid otherwise.

For EdDSA, we have enhanced security and where we overcome some of the problems of ECDSA. With this, Alice creates a SHA-512 hash of her private key:

h=HASH(sk)

Create r from the upper 32 bytes of hash and the message:

r=HASH(h[32:]||m))

And where “||” represents a concatenation of the byte array values. Next she matches r onto curve with:

R=rB

Next Alice computes s with:

s=r+(HASH(R||pk||m))⋅sk

The signature is (R,s). The values of R and s are 32 bytes long, and thus the signature is 64 bytes long.

Bob creates S using R, pk and m:

S=HASH(R||pk||m)

And next creates two verification values:

v_1=sB

v_2=R+pkS

If v_1==v_2

the signature checks. With the generation of the private key and the public key, we will generate our private key related to the size of the curve.

Next some code [here]:

namespace ECDSA
{
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
using System.Security.Cryptography;
class Program
{

static void Main(string[] args)
{



var curvename="secp256k1";
var signtype="SHA1withECDSA";
var msg="Hello";
if (args.Length >0) msg=args[0];
if (args.Length >1) curvename=args[1];
if (args.Length >2) signtype=args[2];



try {

X9ECParameters ecParams = ECNamedCurveTable.GetByName(curvename);
var curveparam = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());

Org.BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters keygenParams = new Org.BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters (curveparam, new SecureRandom());
Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator generator = new Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator("ECDSA");
generator.Init(keygenParams);
var keyPair = generator.GenerateKeyPair();
// Create signature
var signer = SignerUtilities.GetSigner(signtype);
var privateKey = (ECPrivateKeyParameters) keyPair.Private;
var publicKey = (ECPublicKeyParameters) keyPair.Public;
signer.Init(true, privateKey);

signer.BlockUpdate(System.Text.Encoding.ASCII.GetBytes(msg), 0, msg.Length);

// Verify signature
var signer2 = SignerUtilities.GetSigner(signtype);
signer2.Init(false, publicKey);
signer2.BlockUpdate(System.Text.Encoding.ASCII.GetBytes(msg), 0, msg.Length);
var rtn=signer2.VerifySignature(signer.GenerateSignature());

Console.WriteLine("== Curve: {0} ",curvename);
Console.WriteLine("== Message: {0} ",msg);
Console.WriteLine("== Signature type: {0} ",signtype);
Console.WriteLine("\n== Signature === ");
Console.WriteLine("== Signature: {0} [{1}] ",Convert.ToHexString(signer.GenerateSignature()),Convert.ToBase64String(signer.GenerateSignature()));
Console.WriteLine("== Verified: {0} ",rtn);
Console.WriteLine("\n== Private key === ");
Console.WriteLine("== D ==={0} ",privateKey.D.ToString());
Console.WriteLine("\n== Public key === ");
Console.WriteLine("== Q_x ==={0} ",publicKey.Q.XCoord);
Console.WriteLine("== Q_t ==={0} ",publicKey.Q.YCoord);

Console.WriteLine("\n\nCurve details: G={0}, N={1}, H={2}", ecParams.G, ecParams.N, ecParams.H);
Console.WriteLine("A={0}\nB={1}\nField size={2}",ecParams.Curve.A,ecParams.Curve.B,ecParams.Curve.FieldSize);

} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}

}

A sample run for secp2561 with SHA512 ECDSA gives [here]:

== Curve: secp256k1 
== Message: Hello 1234
== Signature type: SHA512withECDSA
== Signature ===
== Signature: 30450220378FA5A081E34BF2BBB6A72B9EBD17416CDF1ADCD2801F9B2E1582068D23DDBB022100C481964EC325974516797168BC705ED1EF5D5A02B70DFE3F1338146D0AE24075 [MEUCIQCf8NGsOSdmQR89azDKLDr84zcQ1mv+oPFgSW/B4WzCgQIgOFPyfByScpD6AtxIaDsx0CREuyNyJ0NSW8oBJU2a+Ik=]
== Verified: True
== Private key ===
== D ===69492866219808339828853746543402782282399383887441314311192822336293855185015
== Public key ===
== Q_x ===f56de6285596136aab932f9b5f4ca78340d166ce142b93f29f4c8e32eca1f371
== Q_t ===7a515b2e8aeaa771ea5340303ceff36b6514d0b754340497fdd40452a07d0cb6

Curve details: G=(79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,1,0), N=115792089237316195423570985008687907852837564279074904382605163141518161494337, H=1
A=0
B=7
Field size=256

EdDSA

For EdDSA, we have enhanced security and where we overcome some of the problems of ECDSA. With this, Alice creates a SHA-512 hash of her private key:

h=HASH(sk)

Create r from the upper 32 bytes of hash and the message:

r=HASH(h[32:]||m))

And where “||” represents a concatenation of the byte array values. Next she matches r onto curve with:

R=rB

Next Alice computes s with:

s=r+(HASH(R||pk||m))⋅sk

The signature is (R,s). The values of R and s are 32 bytes long, and thus the signature is 64 bytes long.

Bob creates S using R, pk and m:

S=HASH(R||pk||m)

And next creates two verification values:

v_1=sB

v_2=R+pkS

If v_1==v_2

the signature checks. With the generation of the private key and the public key, we will generate our private key related to the size of the curve.

The code is [here]:

namespace EdDSA
{

using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;


class Program
{

static void Main(string[] args)
{


var msg="Hello";


if (args.Length >0) msg=args[0];



try {



Org.BouncyCastle.Crypto.Parameters.Ed25519KeyGenerationParameters keygenParams = new Org.BouncyCastle.Crypto.Parameters.Ed25519KeyGenerationParameters (new SecureRandom());

Org.BouncyCastle.Crypto.Generators.Ed25519KeyPairGenerator generator = new Org.BouncyCastle.Crypto.Generators.Ed25519KeyPairGenerator();
generator.Init(keygenParams);
var keyPair = generator.GenerateKeyPair();

// Create signature
var signer = SignerUtilities.GetSigner("Ed25519");

var privateKey = (Ed25519PrivateKeyParameters) keyPair.Private;
var publicKey = (Ed25519PublicKeyParameters) keyPair.Public;


signer.Init(true, privateKey);

signer.BlockUpdate(System.Text.Encoding.ASCII.GetBytes(msg), 0, msg.Length);

// Verify signature
var signer2 = SignerUtilities.GetSigner("Ed25519");
signer2.Init(false, publicKey);
signer2.BlockUpdate(System.Text.Encoding.ASCII.GetBytes(msg), 0, msg.Length);
var rtn=signer2.VerifySignature(signer.GenerateSignature());



Console.WriteLine("Ed25519");
Console.WriteLine("== Message: {0} ",msg);

Console.WriteLine("\n== Signature === ");
Console.WriteLine("== Signature: {0} [{1}] ",Convert.ToHexString(signer.GenerateSignature()),Convert.ToBase64String(signer.GenerateSignature()));
Console.WriteLine("== Verified: {0} ",rtn);

Console.WriteLine("\n== Private key === ");
Console.WriteLine("== Private key ==={0} ",Convert.ToHexString(privateKey.GetEncoded()));
Console.WriteLine("\n== Public key === ");
Console.WriteLine("== Public key ==={0} ",Convert.ToHexString(publicKey.GetEncoded()));



} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}



}

and a sample run [here]:

Ed25519
== Message: Hello 123

== Signature ===
== Signature: 8990D0235338EEB7073FD67E73A4A024EFB7BD2B660241B59AF1A3C2B418EBD180743457FEA46DCFE8382D6D2D1DBED8F5A7F041E9A64D99088366444B94080A [iZDQI1M47rcHP9Z+c6SgJO+3vStmAkG1mvGjwrQY69GAdDRX/qRtz+g4LW0tHb7Y9afwQemmTZkIg2ZES5QICg==]
== Verified: True

== Private key ===
== Private key ===4EE34144917A85796FC6E5998EA289A2CCF3256B6623F061DA9C38E73EEED10D

== Public key ===
== Public key ===918801730AAF396BF05792BC9F704BFA1E188B2185E2D2000C5BB0560BA984E6

Conclusions

If Satoshi was to create Bitcoin now, it is likely that he/she would now use EdDSA, as it allows for better scalability and is simplier. But the development of elliptic curves has been mitered with patents. Now, though, we are clear of these problems, and thus EdDSA looks to beat ECDSA in most respects.