Link to this headingElliptic Curve Digital Signature Algorithm (ECDSA)
- Biased on the [ElGamal](/Crypto/Asymmetric Encryption/ElGamal) signature scheme
- Good versions should use Deterministic randoms from secure hash functions
The signing encodes a Random Point using the private_key and the message hash. This is proof that the signer know the public key.
Link to this headingUsage
Using python-ecdsa:
Link to this headingWhy the signature works
Signature Equation:
s = k^{−1} ∗ (h + r ∗ privKey) \pmod{N}
Signature Equalities:
s_1 = s^{-1} \pmod{N}
= (k^{−1} ∗ (h + r ∗ privKey))^{−1} \pmod{N}
= k ∗ (h + r ∗ privKey)^{−1} \pmod{N}
Random Point Equalities:
R_1 = (h * s_1) * G + (r * s_1) * pubKey
= (h * s_1) * G + (r * s_1) * privKey * G
= (h + r * privKey) * (k ∗ (h + r ∗ privKey)^{−1} \pmod{N}) * G
= k * (h + r * privKey) * ((h + r ∗ privKey)^{−1} \pmod{N}) * G
= k * G
Link to this headingGenerating a Keypair
- The Public Key is a compressed x,y coordinated with a parity bit.
- The Private Key is a random 256 bit number with a parity bit.
Generating the Keypair:
# The Private key is just a random number mod the Modulus
= +1
# public_key = private_key * G
=
Link to this headingSigning a Message
- Calculate the Hash of the message
h
- Generate a random number range [1..n-1] and store it as the private key
- Generate k
- Deterministic-ECDSA, k = HMAC(h + private_key)
- Non Deterministic-ECDSA, k = message_key
- Calculate the random point R = k * G
- Set r = R.x
- Calculate the signature
s = k^{-1} * (h + r * private_key) \pmod{N}
k^{-1} is where (k * k^{-1}) mod N = 1
- Return the signature {r, s}.
Signatures are twice as big as the Private key length. This is because both r and s are the same size as the private key.
Link to this headingImplementation
Non Deterministic Signature:
### ECDSA
#Derive Information
=
=
#Hash the message
=
#Generate Random and make a new Point
, =
#Get Inverse of the message_privatekey
=
#Generate S
# s = (k^-1 * (hash + (private_mult * x_point_k)) % order ) % order
= %
return
= b
#Generate KeyPair
, =
#Create Signature
=
#{'message_x': 33713788222053272944116994952373133006816574331840523836593195672659427182416, 'signature': 29762850178583845985260513726938610357122753240601859968748061248677808151}
Deterministic Signature:
### Deterministic ECDSA
#Derive Information
=
=
= *
#Hash the message
=
=
#Generate Random and make a new Point
=
= *
#Get Inverse of the message_privatekey
=
#Generate S
# s = (k^-1 * (hash + (private_mult * x_point_k)) % order ) % order
= %
return
= b
#Generate KeyPair
, =
#4939898551211325557705050936134227494084736863309989380729247740376583402505 Curve25519: x=15059783236178707571372921326432572117947812618812556902447203212690639774217, y=27261639159700502897208347171409964976166642449687224613599179064654646974491
#### Deterministic ECDSA
#Create Signature
=
#{'message_x': 31021640711082967606874736653412318084900442268616466260267648860639766810380, 'signature': 5394634105920027034974249426205873664623852938893490093540580448939351731001}
#Check Signature
=
#True
Link to this headingVerifying a Message
- Calculate the Hash of the message
- Calculate the modular inverse of the signature proof
s^{-1}.
s^{-1} is where (s * s^{-1}) mod N = 1
- Generate the random point used during signing
R = (h * s^{-1}) * G + (r * s^{-1}) * pubKey
- Set
r_1 = R.x
- Validate that the
r_1 from the calculation is the same as the provided r
The general idea of the signature verification is to recover the point R’ using the public key and check whether it is same point R, generated randomly during the signing process.
Link to this headingImplementation
Non-Deterministic Verify:
#Derive Information
=
=
#Hash message
= %
#Use the inverse of the signature
=
#Generate Points from x value
=
=
#print(points)
#R' = (h * s1) * G + (r * s1) * pubKey
= * +
#Because of the way this is checked there are two possible public keys. The one that is used and the negative point
return ==
= b
#Generate KeyPair
, =
#Create Signature
=
#{'message_x': 33713788222053272944116994952373133006816574331840523836593195672659427182416, 'signature': 29762850178583845985260513726938610357122753240601859968748061248677808151}
#Check Signature
=
#True
Deterministic Verify:
### Deterministic ECDSA
#Derive Information
=
=
#Hash the message
=
=
#Use the inverse of the signature
=
#R' = (h * s1) * G + (r * s1) * pubKey
= * +
#Because of the way this is checked there are two possible public keys. The one that is used and the negative point
return ==
= b
#Generate KeyPair
, =
#4939898551211325557705050936134227494084736863309989380729247740376583402505 Curve25519: x=15059783236178707571372921326432572117947812618812556902447203212690639774217, y=27261639159700502897208347171409964976166642449687224613599179064654646974491
#### Deterministic ECDSA
#Create Signature
=
#{'message_x': 31021640711082967606874736653412318084900442268616466260267648860639766810380, 'signature': 5394634105920027034974249426205873664623852938893490093540580448939351731001}
#Check Signature
=
#True
Link to this headingGetting a Public Key from a message and a Signature
- Using the Signature and the message it is possible to get 0-2 public keys to test.
Implementation:
return
#Make Hash and Signature of first JWT
=
=
=
#Make Hash and Signature of second JWT
=
=
=
#Get Possoble keys from signatures
=
=
#Find the common Key
return
Link to this headingAttacks
- Recovering the Initial Random (k) from Signature will allow recovery of the private key
Link to this headingWhen the random (m) is the same for two different signatures
The Math Behind the attack:
random\_output_1 = (random\_number_1 * G) \\
signature_1 = \dfrac{(random\_output_1 * private\_key) + message_1}{random\_number_1}
\begin{aligned}
random\_output_2 &= (random\_number_2 * G) \\
&= random\_output_1 * signature_2 \\
&= ((random\_output_2 * private\_key) + message_2) /random\_number_2 \\
\end{aligned}
\\
\begin{aligned}
signature_1 - signature_2 &= \dfrac{(random\_output_1 * singing\_key) + message_1}{random\_number} - \dfrac{(random\_output_2 * singing\_key) + message_2}{random\_number} \\
&= \dfrac{(random\_output_1 * signing\_key) - (random\_output_2 * singing\_key) + message_1 - message_2}{random\_number} \\
&= \dfrac{ message_1 - message_2}{random\_number}
\end{aligned}
\newline
\newline
\begin{aligned}
signing\_key &= \dfrac{random\_number * signature_1 - message_1}{random\_output_1} \\
& = \dfrac{message_1 * signature_2 - message_2 * signature_1}{random\_output_1} \\
& = \dfrac{signature_1 * \dfrac{message_1 - message_2}{signature_1 - signature_2} - message_1}{random\_output_1} \\
& = \dfrac{\dfrac{message_1 - message_2}{1 - signature_2} - message_1}{random\_output_1} \\
& = \dfrac{\dfrac{message_1 - message_2}{1 - signature_2} - \dfrac{message_1 - message_1 * signature_2)}{1 - signature_2}}{random\_output_1} \\
& = \dfrac{ message_1 * signature_2 - message_2}{random\_output_1 - signature_2} \\
\end{aligned}
Implementation:
=
#Get the subtraction of the hash values
= -
#Split signature into r,s
, =
, =
#Start Math
#Get the subtraction of the s values
= -
#Get the Multiplicative Inverse Mod N
=
#Multiply the values
#k = (s1 – s2)-1(H(m1) – H(m2))
= %
#Get Inverse to generate the private exp
=
#d = (((s1 * k - h1) % N) * r_inv) % self.n
= %
=
=
return
#priv_key = generateSigningKey(hash_1, signature1, hash_2, signature2, verify_key)
Link to this headingBad Randomness in Nonces
Papers:
- Lattice Attacks againstWeak ECDSA Signatures in Cryptocurrencies
- LadderLeak attack
- LadderLeak attack
Link to this headingTiming Attacks Leaking the random (k)
Since the random is multiplied with the generator. And Multiplication uses the plus, square method you can leak the binary data of the secret.
Link to this headingSignature Malleability
Signature Malleability is if the signature integer is not checked that it is between 0 and the order of the curve. Because the integer is modded you can have a lot of numbers for the
Link to this headingZero Padding
If there is zero padding in the bytes form of the data then it will output the same number which is still the same number
Example:
#from pycoin.ecdsa import secp256k1_generator, sign, verify
#print(dir(secp256k1_generator))
=
return
=
=
return
=
=
return
# ECDSA sign message (using the curve secp256k1 + SHA3-256)
=
=
#Generate Random Number between 0, Generator Order
=
=
=
# ECDSA with prepadded signature data
=
=
=
"""
Message: Message for ECDSA signing
Private key: 0x6d193a1a2b66676b983a59a02438d7d913810865d694455ccda94c656719e410
Signature: r=0xfb60b669b2b68d7a4e23512dad6afea14a18cfd8716ceccd70f17575063fc2a4, s=0x7b5f23a85d65d5f5cfce32c692dc1e52976af586807a1669d3581b9eb2e14658
Message: Message for ECDSA signing
Signature: r=0xfb60b669b2b68d7a4e23512dad6afea14a18cfd8716ceccd70f17575063fc2a4, s=0x7b5f23a85d65d5f5cfce32c692dc1e52976af586807a1669d3581b9eb2e14658
Signature valid? True
"""
Link to this headingDuplicate signatures
ECDSA is mathematically malleable as well. It consists of two values (r, s) and given one signature it is trivial to calculate a new s’ (equal to the order minus the original s) which results in a new valid signature (r, s’) over the same message.
Example:
= b
#Generate KeyPair
, =
#Message Information
#Hash Library
#Create Signature
=
#{'message_x': 28976191879944715029002043232329621948852541633267460398860125146443746557606, 'signature': 4980853891321906837859819475022323023771859670224468501147526911371058887087}
#Check Signature
=
#True
#Test Duplicate Signature
# s = n - s
= -
=
#True
Link to this headingInvalid Curve Parameters
If a client or server just accepts any curve messing with the parameters can make decryption easy.
Link to this headingLattice Attacks
Biased Nonce Sense: Lattice Attacks against Weak ECDSA Signatures in Cryptocurrencies
CTF Describing the Attack
Lattice ECDSA Attack PoC
- Needs atleast 4 LSB or MSB bits set
Link to this headingCVE-2022-21449: Psychic Signatures
https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/
Many error happened to make this bug.
- No error checking if
r >= 1 and s >= 1. Also no checking that r < n and s < n
- No check with fermats therum to create the inverse of
s.
- No check that the point is the infinity point.
When the r = 0 or s = 0 then there is the possibility that the modular inverse will be incorrect.
Link to this heading2 Party Signing (Lindell17)
- Alice generates a random x_1 and a secret key k_1.
- Bob generates a random x_2 and a secret key k_2.
- Bob encrypts x_2 to and sends it to Alice
- Alice using hobomorphic encryption to interact with Bobs x_2 to generate
Enc((k_1^{-1} % l) * (msg + x_1 *x_2))
- This is sent to Bob to finish the signature. He decrypts the blob
(k_1^{-1} % l) * (msg + x_1 *x_2).
- Then finalizes the signature
s = k_2^{-1} * (k_1^{-1} % l) * (msg + x_1 *x_2) % l
- Check the signature
Link to this headingImplementation
Link to this headingAttack
By setting the k_1 to a custom value the target leaks if k_1 is divisible by x_2. For example if k_1 is 2 then you get the LSB of x_2.
Extracting the ith bit:
k_1 = 2^i
offset = (k_1^{-1} \mod{l} - k_1^{-1} \mod N) * (msg + x_1 * partial_x_2)
Link to this heading2+ Party Signing (GG18/GG20)