Photo by Cytonn Photography on Unsplash

With a digital signature, we sign a message with a private key (sk), and then prove it with the related public key (pk). The signature normally takes the form of (r,s). In this case we will generate signatures for the main methods used in ECDSA and EdDSA.

For ECDSA, Alice signs the message with the following:

Bob will check with:

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. For example for a 192-bit curve (eg NIST P192) we have 24 bytes of random data:

sk = ECPrivateKey(secrets.randbits(32*8), curve)
if (curve.size==192):
sk = ECPrivateKey(secrets.randbits(24*8), curve)
elif (curve.size==224):
sk = ECPrivateKey(secrets.randbits(28*8), curve)
elif (curve.size==160):
sk = ECPrivateKey(secrets.randbits(20*8), curve)
elif (curve.size==448):
sk = ECPrivateKey(secrets.randbits(56*8), curve)

We can then generate the public key based on the private key:

pk = sk.get_public_key()

The code used is [here]:

from ecpy.curves import Curve
import secrets
from ecpy.keys import ECPrivateKey
from ecpy.ecdsa import ECDSA
from ecpy.eddsa import EDDSA
import hashlib
from ecpy.formatters import decode_sig
import sys,binascii
curve = Curve.get_curve('secp256k1')G = curve.generator
order = curve.order
t=0
msg = 'hello'
if (len(sys.argv)>1):
msg=str(sys.argv[1])
if (len(sys.argv)>2):
t=int(sys.argv[2])
msg=msg.encode()if (t==1):
curve = Curve.get_curve('NIST-P192')
elif (t==2):
curve = Curve.get_curve('NIST-P224')
elif (t==3):
curve = Curve.get_curve('NIST-P256')
elif (t==4):
curve = Curve.get_curve('Ed25519')
elif (t==5):
curve = Curve.get_curve('secp192k1')
elif (t==6):
curve = Curve.get_curve('secp160k1')
elif (t==7):
curve = Curve.get_curve('secp224k1')
elif (t==8):
curve = Curve.get_curve('Brainpool-p256r1')
elif (t==9):
curve = Curve.get_curve('Brainpool-p224r1')
elif (t==10):
curve = Curve.get_curve('Brainpool-p192r1')
elif (t==11):
curve = Curve.get_curve('Brainpool-p160r1')
elif (t==12):
curve = Curve.get_curve('secp256r1')
elif (t==13):
curve = Curve.get_curve('Ed448')
if (t!=4 and t!=13):
print (f"Name: {curve.name}, y^2=x^3+a*x+b (mod p) Type: {curve.type}, Size: {curve.size}, a={curve.a}, b={curve.b}, G={curve.generator}, field={curve.field}, order={curve.order}")
else:
print (f"Name: {curve.name}, a*x^2+y^2=1+d*x^2*y^2 (mod p) Type: {curve.type}, Size: {curve.size}, a={curve.a}, d={curve.d}, G={curve.generator}, field={curve.field}, order={curve.order}")
sk = ECPrivateKey(secrets.randbits(32*8), curve)
if (curve.size==192):
sk = ECPrivateKey(secrets.randbits(24*8), curve)
elif (curve.size==224):
sk = ECPrivateKey(secrets.randbits(28*8), curve)
elif (curve.size==160):
sk = ECPrivateKey(secrets.randbits(20*8), curve)
elif (curve.size==448):
sk = ECPrivateKey(secrets.randbits(56*8), curve)
pk = sk.get_public_key()
if (t==4):
signer = EDDSA(hashlib.sha512)
pk = signer.get_public_key(sk, hashlib.sha512)
sig = signer.sign(msg,sk)
rtn=signer.verify(msg,sig,pk)
elif (t==13):
signer = EDDSA(hashlib.shake_256, hash_len=114)
pk = signer.get_public_key(sk, hashlib.shake_256, hash_len=114)
sig = signer.sign(msg, sk)
rtn = signer.verify(msg, sig, pk)
else:
signer = ECDSA()
sig = signer.sign(msg,sk)
rtn=signer.verify(msg,sig,pk)
print("Message: ",msg)
print("\nPrivate key:", hex(sk.d))
print(f"Public key: ({hex(pk.W.x)},{hex(pk.W.y)}")
print(f"Signature verification: {rtn}")
print("\nSignature:", binascii.hexlify(sig).decode())
if (t!=4 and t!=13):
sig_len = sig[1]+2
r_offset = 4
r_len = sig[3]
s_offset = 4+r_len+2
s_len = sig[4+r_len+1]
r = int.from_bytes(sig[r_offset:r_offset+r_len], 'big')
s = int.from_bytes(sig[s_offset:s_offset+s_len], 'big')
print (f"\n(r,s) = ({r},{s})")
r,s = decode_sig(sig, fmt='DER')
print (f"\n(r,s) - Check = ({r},{s})")
else:
l = len(sig)>>1
r = int.from_bytes(sig[0:l], 'little')
s = int.from_bytes(sig[l:], 'little')
print (f"\n(r,s) = ({r},{s})")

A sample run [here]:

Name: secp256k1, y^2=x^3+a*x+b (mod p) Type: weierstrass, Size: 256, a=0, b=7, G=(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 , 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8), field=115792089237316195423570985008687907853269984665640564039457584007908834671663, order=115792089237316195423570985008687907852837564279074904382605163141518161494337
Message: b'hello'
Private key: 0x5d1ab21f17f6caed1c29d6cc019cf7dda1071cc4b37dcba654c47af98cc99a34
Public key: (0xda92763477b34b6443cca901a1bc794e7c65b85c5cc6e9d443e992b7062adf84,0x56cc5919829f80b80a2f91806d2bf565a033f91e25e7fc052d1cb71167ed2fd6
Signature verification: True
Signature: 304402200c3b07c8b068eca31eb15cf8a608737855051e9582a40ed889e53815ecada901022069b84613fe6bbd8220d2509f7c42e19da1a95f45770d22b77c48d0bf7ec4ce27(r,s) = (5532051882613009013454805533407030651295281449899719376852625570987602979073,47818432622439099370009880209558293062203021294593318635492502307350283341351)

and Ed448 [here]:

Name: Ed448, a*x^2+y^2=1+d*x^2*y^2 (mod p) Type: twistededward, Size: 448, a=1, d=726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358, G=(0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e , 0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14), field=726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018365439, order=181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779
Message: b'hello'
Private key: 0x31a037add317c687b4765ad8db14cda8d016d02d328c1d85b2a3e39bc6727b9e2aaeb3e64cf0cbecc9ad4d1ceb755c7d8589f5d867efbb04
Public key: (0x972e36a0e3c8d1fd7d56e4d97987733e617283751e44a10fef88446ae4d043d011a1dd2984ad3fa352e7e735d8dd5d162fe4e02aec331d7,0xb6014fd194985443b584aa938308ced3e08395447358d80aced4560d60f26a5b78f5952b89373d482a135c3bd189fc988632721d956d336c
Signature verification: True
Signature: 5f5a3e0fa856adaa5364e6286597ead0447089079f865959981d4168b1f610b51fd3a26cf748f7d9e105da77796573bcc3fa3af4f5456c1e80bc305cf9e4252a392dc157e8d43d90f0300a38f5648f2641a98d80aeb8ca6d9519d758f7f8a0a6c9da9d0f294fb215c2436c5022ee6f7f3900(r,s) = (93121733947056327907665489481668249791943809936437750349071038045653315352609199077452776334031756827652939852030325331545642803985799775,163248550059559477898666947236960607854615136440383872090895197693307102325686319248853139683367591735022090614536751873847799383339196)

Professor of Cryptography. Serial innovator. Believer in fairness, justice & freedom. EU Citizen. Auld Reekie native. Old World Breaker. New World Creator.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store