Pre-script: This article is not intended for newbies, knowledge of Ethereum and/or solidity is presumed.
Transactions on Ethereum are signed messages originating from Externally Owned Accounts (EOAs). There are more to this simple description though, as not all of such messages are valid transactions. Valid transactions must contain these required parameters: Nonce, Recipient, Value, Data, Gas price, Gas limit, Chain Id and v, r, s, and the transaction must be signed using the EOA’s private key (and we’ll see why shortly).
Understanding the concept of digital signatures without prior understanding of the components of that signature may be a bit much for some people, so we’ll first try to explain some of these concepts (of course these concepts are well detailed in the Ethereum book).
Nonce is an acronym for Number used ONCE. It’s an integer that keeps track of the number of messages signed and transmitted from an account. It increments per transaction, and a nonce cannot be used more than once. Any transaction signed with an already used nonce is rejected by the network.
Value is the amount of ether or token forwarded with the message. Transactions containing only values are known as ‘payments’.
Data is the payload forwarded with the transaction. These are mostly function calls (payload containing function selector) directed to contract accounts, as most EOAs do not contain bytecodes and hence cannot handle such calls. Transactions containing only data are known as ‘invocations’. Aside from these, transactions can also contain both data and value, or nothing at all.
Chain ID represents the blockchain network to which the transaction is to be sent. The use of chain id means that transactions created for one blockchain cannot be valid on another blockchain. This protects transactions from replay attacks, as transactions become invalid if sent to another network. (see EIP-155 for more on this).
v, r, s are signature variables generated from EOA’s private keys and used for digital signatures creation and verification. (More on this shortly).
The Idea of Private keys and Public keys
Private keys are a secret set of randomly generated integers used in cryptography for creating public keys and signing transactions. These keys are neither required nor transmitted on Ethereum network, they are sole properties and responsibilities of EOAs, and anyone with access to them has access to all the accounts generated from that private key.
Now, how do we generate public keys from private keys? Magic.
Ethereum makes use of public key cryptography, also known as asymmetric cryptography, to create public-private key pairs. Elliptic curve cryptography (which is a form of asymmetric cryptography) is used to calculate the public key from the private key in an irreversible equation using the elliptic curve algorithm secp256k1.
Two things to bear in mind:
i) ‘A public key is a point on the elliptic curve, meaning it is a set of x and y coordinates that satisfy the elliptic curve equation’. Hence public keys are two numbers joined together, each number representing a point on the elliptic curve.
ii) The elliptic curve algorithm used to calculate these points is a one way function. In practice it is very easy to calculate the public key from a private key, but impossible to reverse the process and recover the private key that generated that public key. This means that the algorithm cannot be reversed to get a private key that generated a public key. Not even quantum computers have been able to achieve this.
Transaction serialization
Now that we understand the concept of public and private keys and how they are generated, let us go a step further to explain how transactions are serialized before they are broadcasted to the network.
Serialization is the concept of preparing signed messages for broadcast to the Ethereum network by using a particular function (algorithm) to create a particular sequence of bytes. The function used for formatting the transaction is a generally accepted standard on the network, such that transactions formatted with this standard are accepted on the blockchain irrespective of the library, language or application used by any particular node. On the Ethereum network the standard used for this purpose is the RLP encoding algorithm (the Recursive-length prefix algorithm).
Concept of Digital Signatures
Digital signatures are synonymous with traditional official signatures. It’s simply a way to authorize a transaction or message on the internet. Any signed transaction contains a digital signature, and the signature is a proof of validity of such message.
Digital signatures are very important because they provide a proof of the account where a transaction originates from (tx.origin in Solidity), and the signer of such a transaction cannot deny that the signature originated from his account. Digital signatures also provide proof that the content of a message or transaction is not tampered with, because any change in the content of the message will produce a signature entirely different from the original one.
How Digital Signatures are Created
On the lower level, digital signatures are generated from Elliptic Curve Digital Signature Algorithm ( ECDSA ), and this algorithm consists of two parts. The first part is the signature creating algorithm, while the second part is the signature verifying algorithm.
Digital signatures are created when a private key signs an already serialized transaction. Actually the serialized transaction here is the Keccak256 hash of the RLP-encoded message. The mathematical function for signing transaction is:
S i g = Fsig(Fkeccak256(m), k)
where: k = the signing private key
m= the RLP encoded message
Fkeccak256 = Keccak256 hash function
Fsig = the signing algorithm
Sig = the resulting signature
The function Fsig produces a signature (S i g) that generates the two values r and s, which are instrumental in signature verification. S i g = r, s
(more on this in the Ethereum book referenced below).
I found this explanation by Svetlin Nakov, (PhD) to be very detailed and thus helpful:
“The ECDSA signing algorithm (RFC 6979) takes as input a message msg *+ a private key privKey *and produces as output a signature, which consists of a pair of integers {r, s}.
…The calculated signature {r, s} is a pair of integers, each in the range [1...n-1]. It encodes the random point R = k * G, along with a proof s, confirming that the signer knows the message h and the private key privKey. The proof s is by idea verifiable using the corresponding pubKey.”
(See reference for more info or links ).
Signature Verification
This is handled by the second part of the ECDSA algorithm. To verify a transaction one must have the signature ( r and s ), the serialized transaction and the public key. This public key must correspond to the private key used in signing the transaction initially. The signature verification algorithm takes these listed components and returns a boolean value depending on if the signature is valid or not; true for valid signature, false for invalid. The mathematical computations and the processes involved are complex and cannot be covered in this article, but below is the summary of the verification process (again, as explained in Svetlin Nakov’s book):
i. Calculate the message hash, with the same cryptographic hash function used during the signing.
ii. Calculate the modular inverse of the signature proof, that is the inverse of signature generation function (see function above).
iii. Recover the random point, r, generated during the signing from the x-coordinate.
iv. Generate from R' its x-coordinate: r' = R'.x
v. Calculate the signature validation result by comparing whether r' == r
“The general idea of the signature verification is to recover the point R' using the public key and check whether it is the same point R, generated randomly during the signing process.”
Again, this is a simple summary of the entire concept of signature generation and verification, by Sveltin Nakov:
“The signing signing encodes a random point R (represented by its x-coordinate only) through elliptic-curve transformations using the private key privKey and the message hash h into a number s, which is the proof that the message signer knows the private key privKey. The signature {r, s} cannot reveal the private key due to the difficulty of the ECDLP problem.
The signature verification decodes the proof number s from the signature back to its original point R, using the public key pubKey and the message hash h and compares the x-coordinate of the recovered R with the r value from the signature.”
The signature is valid if the x-coordinate recovered, r’, is the same as the one randomly generated during the signing, r.
What about v?
As already explained, in public key generation using elliptic curve cryptography, two possible public keys are generated at every instance. This is because the elliptic curve algorithm is symmetry across the x-axis, so for any value of x that is generated, there are two possible values that fit the curve; one on each side of the x-axis.
Question now is, how does this algorithm pick the correct value of x to concatenate with the y value? This is where v comes in.
According to the Ethereum Yellow paper, “ v is a 1 byte value specifying the parity and finiteness of the coordinates of the curve point for which r is the x-value.”
To make it simpler, v is added to the signature verification function to help detect the correct value of r between the two possible generated values R and R’. If v is even, the R is the correct value, if v is odd, then R’.
For more clarification, this is a snippet from the Ethereum book:
“At block #2,675,000 Ethereum implemented the "Spurious Dragon" hard fork, which, among other changes, introduced a new signing scheme that includes transaction replay protection (preventing transactions meant for one network being replayed on others). This new signing scheme is specified in EIP-155. This change affects the form of the transaction and its signature, so attention must be paid to the first of the three signature variables (i.e., v), which takes one of two forms and indicates the data fields included in the transaction message being hashed.
…The special signature variable v indicates two things: the chain ID and the recovery identifier to help the ECDSArecover function check the signature. It is calculated as either one of 27 or 28, or as the chain ID doubled plus 35 or 36.
…The recovery identifier (27 or 28 in the "old-style" signatures, or 35 or 36 in the full Spurious Dragon–style transactions) is used to indicate the parity of the y component of the public key.”
The whole idea of Spurious Dragon upgrade was to mitigate the security challenges of replay attacks and signature malleability, and this was achieved by the introduction of chain ID and recovery identifier into the variable v.
ECRECOVER
ecrecover is a global function in solidity used for signature verification. It takes the massage hash and the signature verification variables v, r, s, and returns an address. The returned address can then be compared against other addresses to find a match.
address signer = ecrecover(msgHash, v, r, s);
SECURITY ALERT: The ecrecover function above is vulnerable to signature replay attack because of the reasons already explained above, so do well to avoid using it in your contracts. Openzeppelin’s ECDSA library is secure and tested for this purpose.
Conclusion
Cryptography is the backbone of blockchain technology. And a public system for value exchange and transactions such as the blockchain should be 100% secure, and should also ensure data integrity, authenticity and confidentiality.
Digital signature is an efficient way to not only keep track of transaction origins, but also to ascertain that transactions are not compromised after they are signed and transmitted. The creation and verification of digital signatures are made possible by the variables v, r, s.
If you find this article interesting, or perhaps cryptography, these resources are available for you.
Resources:
https://ethereum.stackexchange.com/questions/45461/verify-ecdsa-signature-in-solidity -> ECDSA and signature verification implementations
https://ethereum.stackexchange.com/questions/79302/why-do-we-use-serialize-function-of-ethereumjs-tx-package-before-broadcastin -> RLP encoding and tx serialization
https://ethereum.github.io/yellowpaper/paper.pdf -> Ethereum Yellow Paper
https://github.com/ethereumbook/ethereumbook/blob/develop/06transactions.asciidoc#digital-signatures -> Ethereum book: Digital Signatures
https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages -> Sveltin Nakov: Digital signatures
https://medium.com/cryptronics/signature-replay-vulnerabilities-in-smart-contracts-3b6f7596df57 -> Signature Vulnerabilities
Top comments (0)