Skip to content

Commit bc72c9d

Browse files
committed
[#28]:svarga:ecdsa, example with signature verification
1 parent 6ecc2ce commit bc72c9d

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,17 @@ is_point_on_curve(curve.G, curve) # true, G generator point defines the cyc
9090
H("byte size hash of a string") # hashes string to a byte, given the abelian group is byte size
9191
H₁₆("16 bit hash") # in case you need more hash space
9292
H₈("is same") == H("is same") # true, it is just a alias, smae as H8
93+
94+
## ECDSA Signature and Public Key Recovery Example
95+
priv, pub = genkey(curve) # Key generation
96+
msg = "hello ethereum" # Message to sign
97+
signature = sign(curve, priv, msg) # returns a NamedTuple (r = ..., s = ..., v = ...)
98+
is_valid = verify(curve, pub, signature, msg) # Signature verification true
99+
recovered = ecrecover(curve, msg, signature) # Public key recovery from signature
100+
@assert pub == recovered # Check if recovered public key matches original
93101
```
94102

103+
95104
## Installation
96105
```
97106
using Pkg

examples/ecdsa.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
using TinyCrypto
3+
4+
# ─────────────────────────────────────────────────────────────────────────────
5+
# ECDSA Signature and Public Key Recovery Example
6+
# ─────────────────────────────────────────────────────────────────────────────
7+
8+
priv, pub = genkey(curve) # Key generation
9+
msg = "hello ethereum" # Message to sign
10+
signature = sign(curve, priv, msg) # returns a NamedTuple (r = ..., s = ..., v = ...)
11+
is_valid = verify(curve, pub, signature, msg) # Signature verification true
12+
recovered = ecrecover(curve, msg, signature) # Public key recovery from signature
13+
@assert pub == recovered # Check if recovered public key matches originald
14+
15+
# ─────────────────────────────────────────────────────────────────────────────
16+
# The following steps (0–7) manually walk through the ECDSA signing and verification
17+
# process to illustrate what happens under the hood. The goal is to help understand
18+
# the mathematical structure behind the cryptographic operations shown above.
19+
# ─────────────────────────────────────────────────────────────────────────────
20+
21+
# 1. Define a toy Weierstrass curve over 𝔽₃₁ with G of order 37
22+
curve = Weierstrass(31, 6, 9, 37, 1, (0, 3))
23+
# Weierstrass{𝔽₃₁}: y² = x³ + 6x + 9 | G = (0,3), order = 37, cofactor = 1
24+
25+
# 2. Create a mock blockchain transaction
26+
tx = "hello ethereum"
27+
28+
# 3. Hash it (you fake Keccak256 with H₈ → SHA256 first 8 bytes)
29+
z = Int(H₈(tx)) % curve.order # H₈ returns UInt8; convert to Int for modular arithmetic
30+
31+
# 4. Generate keypair
32+
priv = rand(1:curve.order - 1) # private key is a uniformly random integer in [1, curve order − 1]
33+
pub = priv * curve.G # public key is the scalar multiple of the generator: pub = priv · G
34+
35+
r,s,v = 0,0,0
36+
# 5. Sign: create ephemeral scalar `k` and calculate signature (r, s)
37+
while true
38+
k = rand(1:curve.order - 1) # draw a random ephemeral scalar used once per signature
39+
R = k * curve.G # then construct an ephemeral public point R = k·G on the curve
40+
v = isodd(R.point.y.val) ? 1 : 0 # and compute the parity (Ethereum) bit from LSB of y which is used for ECrecover
41+
42+
r = mod(R.point.x, curve.order) # use x-coordinate of R as first signature component
43+
k_inv = invmod(k, curve.order) # compute modular inverse of ephemeral scalar
44+
s = mod(k_inv * (z + r * priv), curve.order) # second signature component s = k⁻¹(z + r·priv) mod n
45+
46+
if r 0 && s 0 break end # loop until suitable `k` is found
47+
end
48+
49+
# 6. The signature is (r, s, v + 27) `27` roots back to early Bitcoin compact signatures, parity bit is used on Ethereum
50+
# whereas Bitcoin uses recovery ID : = {0,1,2,3}
51+
println("Signature: (r = $r, s = $s, v = $(v + 27))")
52+
53+
# 7. Verification
54+
s_inv = invmod(s, curve.order) # compute inverse of signature scalar s⁻¹ mod n
55+
u₁ = z * s_inv % curve.order # u₁ = z · s⁻¹ mod n
56+
u₂ = r * s_inv % curve.order # u₂ = r · s⁻¹ mod n
57+
P = u₁ * curve.G + u₂ * pub # recover point P = u₁·G + u₂·Q (should equal R)
58+
59+
is_valid = mod(P.point.x, curve.order) == r # check if recovered R.x matches r component
60+
println("Signature valid? ", is_valid)

0 commit comments

Comments
 (0)