1- -- Simple Elliptic Curve Cryptography of secp256k1 for encrypting/decrypting messages
1+ -- Simple Elliptic Curve Cryptography of secp256k1 for encrypting/decrypting/signing messages
22-- See https://en.bitcoin.it/wiki/Secp256k1
33
44local bint = require ' bint' (768 )
1717-- Curve prime number
1818local P = bint (' 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' )
1919
20+ -- Curve order
21+ local N = bint (' 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' )
22+
2023-- Curve parameters
2124local A = bint (0 )
2225local B = bint (7 )
@@ -28,6 +31,12 @@ CurvePoint.__index = CurvePoint
2831-- Curve point at infinity
2932local O = CurvePoint {x = bint .zero (), y = math.huge }
3033
34+ -- Curve generator point
35+ local G = CurvePoint {
36+ x = bint (' 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' ),
37+ y = bint (' 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' ),
38+ }
39+
3140-- Checks if points in the curve are equal.
3241function CurvePoint .__eq (a , b )
3342 return a .x == b .x and bint .eq (b .y , b .y )
@@ -82,14 +91,6 @@ function CurvePoint:valid()
8291 return rem :iszero ()
8392end
8493
85- -- Curve generator point
86- local G = CurvePoint {
87- x = bint (' 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' ),
88- y = bint (' 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' ),
89- }
90-
91- -- Curve order
92- local N = bint (' 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' )
9394
9495do -- Tests some curve operations
9596 assert (G :valid ())
@@ -106,45 +107,104 @@ do -- Tests some curve operations
106107 assert (G * 3 == G + G + G )
107108end
108109
109- -- Very simple XOR crypt (very insecure, but works for demo purposes, use a better cipher like AES!)
110- local function xorcrypt (msg , symmetric_private_key )
111- local xor_key = symmetric_private_key .x ~ symmetric_private_key .y
112- return msg ~ xor_key
113- end
110+ do -- Test encrypt and decrypt
111+ -- Very simple XOR crypt (very insecure, but works for demo purposes, use a better cipher like AES!)
112+ local function xorcrypt (msg , symmetric_private_key )
113+ local xor_key = symmetric_private_key .x ~ symmetric_private_key .y
114+ return msg ~ xor_key
115+ end
114116
115- -- Encrypt message using a private symmetric key and the curve public key.
116- local function encrypt (msg , symmetric_private_key , public_key )
117- return xorcrypt (msg , public_key * symmetric_private_key )
118- end
117+ -- Encrypt message using a private symmetric key and the curve public key.
118+ local function encrypt (msg , symmetric_private_key , public_key )
119+ return xorcrypt (msg , public_key * symmetric_private_key )
120+ end
121+
122+ -- Decrypt message using a public symmetric key and the curve private key.
123+ local function decrypt (msg , symmetric_public_key , private_key )
124+ return xorcrypt (msg , symmetric_public_key * private_key )
125+ end
126+
127+ print ' Message encryption test:'
128+
129+ -- Choose a private key
130+ local private_key = bint .frombe (' This is my private key, hide it!' )
131+ print (' private_key = ' .. tostring (private_key ))
132+
133+ -- Compute public key
134+ local public_key = G * private_key
135+ print (' public_key = ' .. tostring (public_key ))
136+ assert (public_key :valid ())
137+
138+ -- Generate a random scalar (ideally should be a random number)
139+ local symmetric_private_key = bint .frombe (' Symmetric key random, hide it!' )
140+ local symmetric_public_key = G * symmetric_private_key
141+ assert (symmetric_public_key :valid ())
119142
120- -- Decrypt message using a public symmetric key and the curve secret key.
121- local function decrypt (msg , symmetric_public_key , secret_key )
122- return xorcrypt (msg , symmetric_public_key * secret_key )
143+ local message = bint .frombe (' Hello world!' )
144+ local encrypted_message = encrypt (message , symmetric_private_key , public_key )
145+ print (' Message: ' .. message :tobe (true ))
146+ print (' encrypted_message = 0x' .. encrypted_message :tobase (16 ))
147+
148+ local decrypted_message = decrypt (encrypted_message , symmetric_public_key , private_key )
149+ print (' Decoded: ' .. decrypted_message :tobe (true ))
150+ assert (message == decrypted_message )
123151end
124152
125- -- Choose a secret key
126- local secret_key = bint .frombe (' This is my secret key, hide it!' )
127- print (' secret_key = ' .. tostring (secret_key ))
153+ do -- Test message signature using EdDSA (see https://en.wikipedia.org/wiki/EdDSA)
154+ print (' Message signature test:' )
155+
156+ -- Simple hash for demo purposes (not very secure, use a better hash like SHA-256!)
157+ local function hash (msg , sign_public_key , public_key )
158+ local function h (v ) return (v << 13 ) ~ (v >> 17 ) ~ (v << 5 ) end
159+ local s = bint .zero ()
160+ local k = bint (' 0x9ddfea08eb382d69' )
161+ s = (h (msg ) ~ s ) * k ; s = s ~ (s >> 47 )
162+ s = (h (sign_public_key .x ) ~ s ) * k ; s = s ~ (s >> 47 )
163+ s = (h (sign_public_key .y ) ~ s ) * k ; s = s ~ (s >> 47 )
164+ s = (h (public_key .x ) ~ s ) * k ; s = s ~ (s >> 47 )
165+ s = (h (public_key .y ) ~ s ) * k ; s = s ~ (s >> 47 )
166+ return s % bint .ipow (2 , 256 )
167+ end
128168
129- -- Compute public key
130- local public_key = G * secret_key
131- assert (public_key :valid ())
132- print (' public_key = ' .. tostring (public_key ))
169+ -- Signs a message using private keys
170+ local function sign (message , sign_private_key , private_key )
171+ local sign_public_key = G * sign_private_key
172+ local public_key = G * private_key
173+ local message_hash = hash (message , sign_public_key , public_key )
174+ local sign_binding_factor = (sign_private_key + private_key * message_hash ) % N
175+ return sign_public_key , sign_binding_factor
176+ end
177+
178+ -- Verifies a message using public signature and public keys.
179+ local function verify (message , sign_binding_factor , sign_public_key , public_key )
180+ local message_hash = hash (message , sign_public_key , public_key )
181+ return sign_public_key + public_key * message_hash == G * sign_binding_factor
182+ end
133183
134- -- Test encrypt and decrypt
135- print ' Message encryption test:'
184+ -- Choose a private key
185+ local private_key = bint .frombe (' This is my private key, hide it!' )
186+ print (' private_key = ' .. tostring (private_key ))
136187
137- -- Generate a random symmetric key (ideally should be a random number)
138- local symmetric_private_key = bint . frombe ( ' Symmetric key random, hide it! ' )
139- local symmetric_public_key = G * symmetric_private_key
140- assert (symmetric_public_key :valid ())
188+ -- Compute public key
189+ local public_key = G * private_key
190+ print ( ' public_key = ' .. tostring ( public_key ))
191+ assert (public_key :valid ())
141192
142- local message = bint .frombe (' Hello world!' )
143- local encrypted_message = encrypt (message , symmetric_private_key , public_key )
144- print (' Message: ' .. message :tobe (true ))
145- print (' encrypted_message = 0x' .. encrypted_message :tobase (16 ))
193+ -- Generate a random scalar (ideally should be a random number)
194+ local sign_private_key = bint .frombe (' Signature random, hide it!' )
195+
196+ local message = bint .frombe (" Hello world!" )
197+ print (' Message: ' .. message :tobe (true ))
198+
199+ -- Sign message
200+ local sign_public_key , sign_binding_factor = sign (message , sign_private_key , private_key )
201+ print (string.format (' signature = (%s, %s)' , sign_public_key , sign_binding_factor ))
202+ assert (sign_public_key :valid ())
203+
204+ -- Verify message
205+ local ok = verify (message , sign_binding_factor , sign_public_key , public_key )
206+ print (' Verification: ' .. tostring (ok ))
207+ assert (ok == true )
208+ end
146209
147- local decrypted_message = decrypt (encrypted_message , symmetric_public_key , secret_key )
148- print (' Decoded: ' .. decrypted_message :tobe (true ))
149- assert (message == decrypted_message )
150210print (' success!' )
0 commit comments