Adjunct Professor @ University of Lausanne

# Blockchain and Distributed Ledgers

Material for the course http://hec.unil.ch/hec/syllabus/descriptif/2471?dyn_lang=en

Last updated: September 30th, 2020
In [10]:
from fastecdsa.curve import secp256k1 as curve

In [2]:
# The generating point G
curve.G

Out[2]:
X: 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
(On curve <secp256k1>)
In [3]:
# The coefficient a in y^2 = x^3 + ax + b
curve.a

Out[3]:
0
In [4]:
# The coefficient b in y^2 = x^3 + ax + b
curve.b

Out[4]:
7
In [5]:
# The number of elments in the underlying Galois field
curve.p

Out[5]:
115792089237316195423570985008687907853269984665640564039457584007908834671663
In [6]:
# The number of solutions (including the neutral element at infinity)
curve.q

Out[6]:
115792089237316195423570985008687907852837564279074904382605163141518161494337
In [26]:
from fastecdsa.curve import secp256k1 as curve
import fastecdsa.keys as keys
from mod import Mod
import hashlib

# every message needs to be hashed into a number
def encrypt_string(hash_str):
return int(hashlib.sha256(hash_str.encode()).hexdigest(), 16)

# The private key is a random number from [1, p - 1] where
# p is the order of the underlying Galois field
private_key = keys.gen_private_key(curve)

# The public key is
public_key = curve.G*private_key

# There are n solutions on the elliptic curve
# including the point at "infinity"
n = curve.q

# We send the clear message...
message = "I love this lecture"

# nonce
i = keys.gen_private_key(curve)

# nonce on elliptic curve
P = curve.G*i

# compute the signature
r = Mod(P.x,n)
inv_i = Mod(i,n).inverse()
s = (inv_i*(encrypt_string(message)+r*private_key))._value
# The sender transmits (r,s), the clear message and his public key
# s depends on the private key and the hashcode of the message.
# However it is not possible to extract the private key from s

# The receiver can now check whether the message is consistent
# This simple test returns False if
# - the text of the message has been changed.
# - if the sent public_key is consistent with  the private key
#   of the sender, without ever revealing the private key

# check the signature
w = Mod(s,n).inverse()
u1 = encrypt_string(message)*w
u2 = r*w

# addition of two residue classes and multiplication with Point
(curve.G*u1 + public_key*u2).x == r
print(i*inv_i)

(1 % 115792089237316195423570985008687907852837564279074904382605163141518161494337)

In [20]:
# We can hide a lot of the complexity
from fastecdsa.curve import secp256k1 as curve
import fastecdsa.keys as keys
from hashlib import sha256
from fastecdsa import ecdsa

private_key = keys.gen_private_key(curve)
public_key = keys.get_public_key(private_key, curve)

# We send the clear message...
message = "I love this lecture"

# standard signature, returns two integers
r, s = ecdsa.sign(message, private_key, curve, hashfunc=sha256)

# should return True as the signature we just generated is valid.
valid = ecdsa.verify((r, s), message, public_key, curve, hashfunc=sha256)
assert valid

# output the signature
print(r)
print(s)
print(public_key)

3621133725411134290833419093700605060079539740192417686493135175327759658361
6925961419047837174706846671373678103227265122461503314642450690352833524666
X: 0xe5f06e980769107cf4e4401eef55df110c108afc71d9558e0ddafb53deaceb81
Y: 0xb02bd7373599708f4ee857c1f403055f4ea01f4844bc08a45a1da2a1081b55d5
(On curve <secp256k1>)

In [28]:
inv = Mod(4,5).inverse()
print(4 * inv)

(1 % 5)

In [ ]: